Commit 26944d9c authored by Florian Blasius's avatar Florian Blasius 🤘

Merge branch '96-list' into 'master'

Resolve "List"

See merge request redox-os/orbtk!116
parents 3e913419 08c563d8
Pipeline #6175 passed with stages
in 12 minutes and 54 seconds
......@@ -19,6 +19,8 @@
brain_*.rs
.DS_Store
Thumbs.db
# Generated by cargo node
static/
\ No newline at end of file
static/
......@@ -49,25 +49,6 @@
"rust"
]
},
{
"name": "Widgets web",
"type": "firefox",
"request": "launch",
"reAttach": true,
"url": "http://localhost:8000",
"webRoot": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Widgets preview",
"program": "${workspaceFolder}/target/debug/examples/widgets",
"cwd": "${workspaceFolder}",
"preLaunchTask": "build preview",
"sourceLanguages": [
"rust"
]
},
{
"type": "lldb",
"request": "launch",
......@@ -101,17 +82,6 @@
"rust"
]
},
{
"type": "lldb",
"request": "launch",
"name": "Light Theme",
"program": "${workspaceFolder}/target/debug/examples/light-theme",
"cwd": "${workspaceFolder}",
"preLaunchTask": "build",
"sourceLanguages": [
"rust"
]
},
{
"type": "lldb",
"request": "launch",
......@@ -134,17 +104,6 @@
"rust"
]
},
{
"type": "lldb",
"request": "launch",
"name": "Grid preview",
"program": "${workspaceFolder}/target/debug/examples/grid",
"cwd": "${workspaceFolder}",
"preLaunchTask": "build preview",
"sourceLanguages": [
"rust"
]
},
{
"type": "lldb",
"request": "launch",
......
This diff is collapsed.
......@@ -21,6 +21,9 @@ edition = "2018"
name = "orbtk"
path = "src/lib.rs"
[profile.dev]
opt-level = 1
[dependencies]
orbtk-api = { version = "0.1.0", path = "crates/api" }
orbtk-css-engine = { version = "0.1.0", path = "crates/css-engine" }
......@@ -34,8 +37,7 @@ dces = { git = "https://gitlab.redox-os.org/redox-os/dces-rust.git" }
[features]
debug = ["orbtk-api/debug"]
preview = ["orbtk-shell/preview", "orbtk-api/preview", "orbtk-render/preview"]
[workspace]
members = [
"crates/api",
......
......@@ -8,6 +8,9 @@ height = 730
[[apps.fonts]]
font_family = "Roboto Regular"
src = "crates/theme/src/fonts/Roboto-Regular.ttf"
[[apps.fonts]]
font_family = "Roboto Medium"
src = "crates/theme/src/fonts/Roboto-Medium.ttf"
[[apps]]
name = "calculator"
......
......@@ -24,6 +24,18 @@ The main goals of OrbTk are speed, ease of use, and being cross platform.
* Cross platform: Redox OS, Linux, macOS, Windows
* CSS theming
## Platforms
* Redox OS (native | cargo-node)
* Linux (native | cargo-node)
* macOS (native | cargo-node)
* Windows (native | cargo-node)
* openBSD (not tested, but should work)
* Web (cargo-node)
* Android (native planned after 0.3 | cargo-node)
* iOS (native planned after 0.3 | cargo-node planned after 0.3)
* Ubuntu Touch (native planned after 0.3 | cargo-node planned for 0.3)
## Usage
To include OrbTk in your project, just add the dependency
......@@ -40,16 +52,6 @@ line to your `Cargo.toml` file:
orbtk = { git = "https://gitlab.redox-os.org/redox-os/orbtk.git" }
```
Note: You also need to have the SDL2 libraries installed on your
system. The best way to do this is documented [by the SDL2
crate](https://github.com/AngryLawyer/rust-sdl2#user-content-requirements).
## Use OrbTk with cairo
* With Ubuntu, execute ```sudo apt-get install libcairo2-dev``` in your console.
* With macOS and homebrew, execute ```brew install cairo``` in your console.
* With macOS and macports, execute ```sudo port install cairo``` in your console.
## Minimal Example
```rust
......@@ -69,7 +71,7 @@ fn main() {
}
```
## Additional Examples
## Run Examples
You can find examples in the `examples/` directory.
......@@ -79,46 +81,38 @@ You can start the widgets example by executing the following command:
cargo run --example widgets --release
```
## Additional Examples on Web
## Run Examples with cargo-node
To run the examples on a browser you have to install
To run the examples on as browser, electron or cordova app you have to install
```text
cargo install -f cargo-web
cargo install -f cargo-node
```
### Run
Before you could use cargo node you have to install `npm` version 6.9.0. It is included in the `Node.js` version 10.16.3. You could download it from https://nodejs.org/dist/v10.16.3/.
You can start the widgets example by executing the following command:
Rust's `cargo` is presumed. All other dependencies of cargo node will be installed automatic.
* Compile to [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly) using Rust's native WebAssembly backend:
### Start examples
```text
cargo web start --target=wasm32-unknown-unknown --auto-reload --example widgets
```
You can start the widgets example by executing the following command:
* Compile to [asm.js](https://en.wikipedia.org/wiki/Asm.js) using Emscripten:
* Run as browser app:
```text
cargo web start --target=asmjs-unknown-emscripten --auto-reload --example widgets
cargo node run --browser --example widgets
```
* Compile to WebAssembly using Emscripten:
* Run as electron app:
```text
cargo web start --target=wasm32-unknown-emscripten --auto-reload --example widgets
cargo node run --electron --example widgets
```
## Run examples with Glutin and Pathfinder
OrbTk includes a preview with [Glutin](https://github.com/rust-windowing/glutin) and [Pathfinder](https://github.com/servo/pathfinder). To start the *preview* mode you have to use the feature *preview*.
If you have problems running OrbTk with cairo on Windows you should try the *preview* feature.
Pathfinder is currently not available for the web.
* Run as cordova app on android:
```text
cargo run --example widgets --release --features preview
cargo node run --android --example widgets
```
## Build and run documentation
......@@ -140,25 +134,25 @@ cargo doc --no-deps --open
* Split application in modules
* Theme update
* Support for Android, iOS, Ubuntu Touch and WebAssembly
* Vulkan / OpenGL Support
* 3D support
## Sub Crates
* api: base api elements of OrbTk e.g. widget and application parts
* css-engine: parse and read values from a css file
* shell: cross platform window and event handling
* theme: OrbTk's default theme (light and dark)
* tree: Tree structure based on DCES
* utils: Helper structs and traits
* widgets: Base widget library
## Dependencies
* [DCES](https://gitlab.redox-os.org/redox-os/dces-rust): Entity Component System
* [rust-cssparser](https://github.com/servo/rust-cssparser): CSS parsing
* [api](https://gitlab.redox-os.org/redox-os/orbtk/tree/master/crates/api): base api elements of OrbTk e.g. widget and application parts
* [css-engine](https://gitlab.redox-os.org/redox-os/orbtk/tree/master/crates/css-engine): parse and read values from a css file
* [render](https://gitlab.redox-os.org/redox-os/orbtk/tree/master/crates/render): cross platform 2D/3D render library
* [shell](https://gitlab.redox-os.org/redox-os/orbtk/tree/master/crates/api): cross platform window and event handling
* [theme](https://gitlab.redox-os.org/redox-os/orbtk/tree/master/crates/theme): OrbTks default theme (light and dark)
* [tree](https://gitlab.redox-os.org/redox-os/orbtk/tree/master/crates/tree): tree structure based on DCES
* [utils](https://gitlab.redox-os.org/redox-os/orbtk/tree/master/crates/utils): helper structs and traits
* [widgets](https://gitlab.redox-os.org/redox-os/orbtk/tree/master/crates/widgets): base widget library
## Inspirations
* [Flutter](https://flutter.io/)
* [React](https://reactjs.org/)
* [Yew](https://github.com/DenisKolodin/yew)
## License
Licensed under MIT license ([LICENSE](LICENSE)).
\ No newline at end of file
......@@ -4,13 +4,11 @@ version = "0.1.0"
authors = ["Florian Blasius <flovanpt@posteo.de>"]
description = "API crate that provides base api and elements for OrbTk like widgets basis."
repository = "https://gitlab.redox-os.org/redox-os/orbtk"
readme = "README.md"
license = "MIT"
keywords = ["ui", "api"]
edition = "2018"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
orbclient = "0.3.24"
[dependencies]
dces = { git = "https://gitlab.redox-os.org/redox-os/dces-rust.git" }
orbtk-css-engine = { version = "0.1.0", path = "../css-engine" }
......@@ -21,5 +19,4 @@ orbtk-tree = { version = "0.1.0", path= "../tree" }
orbtk-theme = { version = "0.1.0", path= "../theme" }
[features]
preview = []
debug = []
\ No newline at end of file
# orbtk-api
Provides base traits and structs for application, widgets and properties. It's part of [OrbTk](https://gitlab.redox-os.org/redox-os/orbtk) - The Rust UI-Toolkit.
[![Build status](https://gitlab.redox-os.org/redox-os/orbtk/badges/master/build.svg)](https://gitlab.redox-os.org/redox-os/orbtk/pipelines)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE)
## Dependencies
* [dces](https://gitlab.redox-os.org/redox-os/dces-rust) (MIT): Entity Component System
## License
Licensed under MIT license ([LICENSE](../../LICENSE)).
\ No newline at end of file
......@@ -4,7 +4,7 @@ use dces::prelude::Entity;
use crate::shell::Key;
#[derive(Default)]
#[derive(Default, Clone)]
/// The `Global` struct is used to define global `properties` that could be access application width.
pub struct Global {
/// Contains the current focused widget.
......@@ -17,7 +17,7 @@ pub struct Global {
pub keyboard_state: KeyboardState,
}
#[derive(Default)]
#[derive(Clone, Default)]
/// Contains the state information for the keyboard.
///
/// This currently tracks which keys are currently pressed.
......
......@@ -42,15 +42,17 @@ impl Application {
let update = Rc::new(Cell::new(true));
let running = Rc::new(Cell::new(true));
let mut context = BuildContext::new(
world.entity_component_manager(),
render_objects.clone(),
layouts.clone(),
handlers.clone(),
states.clone(),
);
let window = {
let mut context = BuildContext::new(
world.entity_component_manager(),
render_objects.clone(),
layouts.clone(),
handlers.clone(),
states.clone(),
);
let window = create_fn(&mut context);
create_fn(&mut context)
};
{
let tree: &mut Tree = world.entity_component_manager().entity_store_mut();
......@@ -119,6 +121,12 @@ impl Application {
.render_context_2_d()
.register_font("Roboto Regular", crate::theme::fonts::ROBOTO_REGULAR_FONT);
#[cfg(not(target_arch = "wasm32"))]
window_shell
.borrow_mut()
.render_context_2_d()
.register_font("Roboto Medium", crate::theme::fonts::ROBOTO_MEDIUM_FONT);
#[cfg(not(target_arch = "wasm32"))]
window_shell
.borrow_mut()
......@@ -137,26 +145,17 @@ impl Application {
});
world
.create_system(EventSystem {
.create_system(EventStateSystem {
shell: window_shell.clone(),
handlers: handlers.clone(),
update: update.clone(),
running: running.clone(),
})
.with_priority(0)
.build();
world
.create_system(StateSystem {
shell: window_shell.clone(),
layouts: layouts.clone(),
mouse_down_nodes: RefCell::new(vec![]),
render_objects: render_objects.clone(),
handlers: handlers.clone(),
states: states.clone(),
update: update.clone(),
running: running.clone(),
layouts: layouts.clone(),
})
.with_priority(1)
.with_priority(0)
.build();
world
......@@ -166,7 +165,7 @@ impl Application {
update: update.clone(),
running: running.clone(),
})
.with_priority(2)
.with_priority(1)
.build();
world
......@@ -179,7 +178,7 @@ impl Application {
update: update.clone(),
running: running.clone(),
})
.with_priority(3)
.with_priority(2)
.build();
world
......@@ -192,7 +191,7 @@ impl Application {
update: update.clone(),
running: running.clone(),
})
.with_priority(4)
.with_priority(3)
.build();
self.runners.push(ShellRunner {
......@@ -208,7 +207,11 @@ impl Application {
/// Starts the application and run it until quit is requested.
pub fn run(mut self) {
while let Some(runner) = self.runners.pop() {
#[cfg(not(target_arch = "wasm32"))]
let mut runner = runner;
#[cfg(target_arch = "wasm32")]
let runner = runner;
runner.run();
}
}
......
......@@ -14,6 +14,7 @@ pub struct WindowAdapter {
pub event_queue: EventQueue,
pub messages: BTreeMap<Entity, Vec<MessageBox>>,
pub root: Entity,
pub mouse_position: Point,
}
pub struct WorldWrapper {
......@@ -33,9 +34,19 @@ impl shell::WindowAdapter for WindowAdapter {
}
fn mouse(&mut self, x: f64, y: f64) {
self.mouse_position = Point::new(x, y);
self.event_queue.register_event(
MouseMoveEvent {
position: Point::new(x, y),
position: self.mouse_position,
},
self.root,
)
}
fn scroll(&mut self, delta_x: f64, delta_y: f64) {
self.event_queue.register_event(
ScrollEvent {
delta: Point::new(delta_x, delta_y),
},
self.root,
)
......@@ -62,6 +73,10 @@ impl shell::WindowAdapter for WindowAdapter {
}
}
fn mouse_position(&self) -> Point {
self.mouse_position
}
fn key_event(&mut self, event: shell::KeyEvent) {
match event.state {
shell::ButtonState::Up => self
......
......@@ -4,4 +4,7 @@ use crate::event::EventBox;
pub trait EventHandler {
/// Handles an `event` by the given `widget`. If it returns `true` the event will not be forwarded.
fn handle_event(&self, event: &EventBox) -> bool;
/// Check if the handler could handle the given event box.
fn handles_event(&self, event: &EventBox) -> bool;
}
......@@ -37,6 +37,10 @@ impl EventHandler for KeyDownEventHandler {
return false;
}
fn handles_event(&self, event: &EventBox) -> bool {
event.is_type::<KeyDownEvent>()
}
}
pub trait KeyDownHandler: Sized + Widget {
......
......@@ -21,6 +21,12 @@ pub struct MouseMoveEvent {
impl Event for MouseMoveEvent {}
pub struct ScrollEvent {
pub delta: Point,
}
impl Event for ScrollEvent {}
pub struct MouseUpEvent {
pub button: MouseButton,
pub x: f64,
......@@ -45,11 +51,11 @@ pub struct MouseDownEvent {
impl Event for MouseDownEvent {}
pub type MouseHandler = dyn Fn(Point) -> bool + 'static;
pub type MouseHandlerFunction = dyn Fn(Point) -> bool + 'static;
/// Used to handle click events. Could be attached to a widget.
pub struct ClickEventHandler {
handler: Rc<MouseHandler>,
handler: Rc<MouseHandlerFunction>,
}
impl Into<Rc<dyn EventHandler>> for ClickEventHandler {
......@@ -66,29 +72,112 @@ impl EventHandler for ClickEventHandler {
return false;
}
fn handles_event(&self, event: &EventBox) -> bool {
event.is_type::<ClickEvent>()
}
}
/// Used to handle mouse down events. Could be attached to a widget.
pub struct MouseDownEventHandler {
handler: Rc<MouseHandlerFunction>,
}
impl Into<Rc<dyn EventHandler>> for MouseDownEventHandler {
fn into(self) -> Rc<dyn EventHandler> {
Rc::new(self)
}
}
impl EventHandler for MouseDownEventHandler {
fn handle_event(&self, event: &EventBox) -> bool {
if let Ok(event) = event.downcast_ref::<MouseDownEvent>() {
return (self.handler)(Point::new(event.x, event.y));
}
return false;
}
fn handles_event(&self, event: &EventBox) -> bool {
event.is_type::<MouseDownEvent>()
}
}
/// Used to handle mouse down events. Could be attached to a widget.
pub struct MouseUpEventHandler {
handler: Rc<MouseHandlerFunction>,
}
impl Into<Rc<dyn EventHandler>> for MouseUpEventHandler {
fn into(self) -> Rc<dyn EventHandler> {
Rc::new(self)
}
}
// pub trait ClickHandler: Sized + From<Template> + Into<Template> {
// /// Transforms the handler into a template.
// fn template<F: FnOnce(Template) -> Template>(self, transform: F) -> Self {
// Self::from(transform(self.into()))
// }
impl EventHandler for MouseUpEventHandler {
fn handle_event(&self, event: &EventBox) -> bool {
if let Ok(event) = event.downcast_ref::<MouseUpEvent>() {
return (self.handler)(Point::new(event.x, event.y));
}
// /// Inserts a handler.
// fn on_click<H: Fn(Point) -> bool + 'static>(self, handler: H) -> Self {
// self.template(|template| {
// template.event_handler(ClickEventHandler {
// handler: Rc::new(handler),
// })
// })
// }
// }
return false;
}
pub trait ClickHandler: Sized + Widget {
/// Inserts a handler.
fn handles_event(&self, event: &EventBox) -> bool {
event.is_type::<MouseUpEvent>()
}
}
pub struct ScrollEventHandler {
handler: Rc<MouseHandlerFunction>,
}
impl Into<Rc<dyn EventHandler>> for ScrollEventHandler {
fn into(self) -> Rc<dyn EventHandler> {
Rc::new(self)
}
}
impl EventHandler for ScrollEventHandler {
fn handle_event(&self, event: &EventBox) -> bool {
if let Ok(event) = event.downcast_ref::<ScrollEvent>() {
return (self.handler)(Point::new(event.delta.x, event.delta.y));
}
return false;
}
fn handles_event(&self, event: &EventBox) -> bool {
event.is_type::<ScrollEvent>()
}
}
pub trait MouseHandler: Sized + Widget {
/// Inserts a click handler.
fn on_click<H: Fn(Point) -> bool + 'static>(self, handler: H) -> Self {
self.insert_handler(ClickEventHandler {
handler: Rc::new(handler),
})
}
/// Insert a mouse down handler.
fn on_mouse_down<H: Fn(Point) -> bool + 'static>(self, handler: H) -> Self {
self.insert_handler(MouseDownEventHandler {
handler: Rc::new(handler),
})
}
/// Insert a mouse up handler.
fn on_mouse_up<H: Fn(Point) -> bool + 'static>(self, handler: H) -> Self {