Draft web runtime and widgets
This commit is contained in:
parent
a97401aed2
commit
27ac85a9d9
16 changed files with 321 additions and 29 deletions
|
|
@ -17,6 +17,7 @@ maintenance = { status = "actively-developed" }
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { version = "0.1.0-alpha", path = ".." }
|
iced = { version = "0.1.0-alpha", path = ".." }
|
||||||
dodrio = "0.1.0"
|
dodrio = "0.1.0"
|
||||||
|
futures = "0.1"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3.27"
|
version = "0.3.27"
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ crate-type = ["cdylib"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced_web = { path = "../.." }
|
iced_web = { path = "../.." }
|
||||||
wasm-bindgen = "0.2.50"
|
wasm-bindgen = "0.2.50"
|
||||||
|
futures = "0.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
console_error_panic_hook = "0.1.6"
|
console_error_panic_hook = "0.1.6"
|
||||||
console_log = "0.1.2"
|
console_log = "0.1.2"
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,35 @@
|
||||||
|
use futures::{future, Future};
|
||||||
|
use iced_web::UserInterface;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
mod tour;
|
||||||
|
|
||||||
|
use tour::Tour;
|
||||||
|
|
||||||
#[wasm_bindgen(start)]
|
#[wasm_bindgen(start)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
console_log::init_with_level(log::Level::Trace)
|
console_log::init_with_level(log::Level::Trace)
|
||||||
.expect("Initialize logging");
|
.expect("Initialize logging");
|
||||||
|
|
||||||
|
let tour = Tour::new();
|
||||||
|
|
||||||
|
tour.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl iced_web::UserInterface for Tour {
|
||||||
|
type Message = tour::Message;
|
||||||
|
|
||||||
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
message: tour::Message,
|
||||||
|
) -> Box<dyn Future<Item = tour::Message, Error = ()>> {
|
||||||
|
self.update(message);
|
||||||
|
|
||||||
|
Box::new(future::err(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&mut self) -> iced_web::Element<tour::Message> {
|
||||||
|
self.view()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
use super::widget::{
|
use iced_web::{
|
||||||
button, slider, Button, Checkbox, Column, Element, Image, Radio, Row,
|
button, slider, text::HorizontalAlignment, Align, Button, Checkbox, Color,
|
||||||
Slider, Text,
|
Column, Element, Image, Radio, Row, Slider, Text,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ggez::graphics::{self, Color, FilterMode, BLACK};
|
|
||||||
use ggez::Context;
|
|
||||||
use iced::{text::HorizontalAlignment, Align};
|
|
||||||
|
|
||||||
pub struct Tour {
|
pub struct Tour {
|
||||||
steps: Steps,
|
steps: Steps,
|
||||||
back_button: button::State,
|
back_button: button::State,
|
||||||
|
|
@ -15,9 +11,9 @@ pub struct Tour {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tour {
|
impl Tour {
|
||||||
pub fn new(context: &mut Context) -> Tour {
|
pub fn new() -> Tour {
|
||||||
Tour {
|
Tour {
|
||||||
steps: Steps::new(context),
|
steps: Steps::new(),
|
||||||
back_button: button::State::new(),
|
back_button: button::State::new(),
|
||||||
next_button: button::State::new(),
|
next_button: button::State::new(),
|
||||||
debug: false,
|
debug: false,
|
||||||
|
|
@ -72,7 +68,7 @@ impl Tour {
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
if self.debug {
|
if self.debug {
|
||||||
element.explain(BLACK)
|
element.explain(Color::BLACK)
|
||||||
} else {
|
} else {
|
||||||
element
|
element
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +88,7 @@ struct Steps {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Steps {
|
impl Steps {
|
||||||
fn new(context: &mut Context) -> Steps {
|
fn new() -> Steps {
|
||||||
Steps {
|
Steps {
|
||||||
steps: vec![
|
steps: vec![
|
||||||
Step::Welcome,
|
Step::Welcome,
|
||||||
|
|
@ -109,19 +105,10 @@ impl Steps {
|
||||||
size_slider: slider::State::new(),
|
size_slider: slider::State::new(),
|
||||||
size: 30,
|
size: 30,
|
||||||
color_sliders: [slider::State::new(); 3],
|
color_sliders: [slider::State::new(); 3],
|
||||||
color: BLACK,
|
color: Color::BLACK,
|
||||||
},
|
},
|
||||||
Step::Radio { selection: None },
|
Step::Radio { selection: None },
|
||||||
Step::Image {
|
Step::Image {
|
||||||
ferris: {
|
|
||||||
let mut image =
|
|
||||||
graphics::Image::new(context, "/ferris.png")
|
|
||||||
.expect("Load ferris image");
|
|
||||||
|
|
||||||
image.set_filter(FilterMode::Linear);
|
|
||||||
|
|
||||||
image
|
|
||||||
},
|
|
||||||
width: 300,
|
width: 300,
|
||||||
slider: slider::State::new(),
|
slider: slider::State::new(),
|
||||||
},
|
},
|
||||||
|
|
@ -183,7 +170,6 @@ enum Step {
|
||||||
selection: Option<Language>,
|
selection: Option<Language>,
|
||||||
},
|
},
|
||||||
Image {
|
Image {
|
||||||
ferris: graphics::Image,
|
|
||||||
width: u16,
|
width: u16,
|
||||||
slider: slider::State,
|
slider: slider::State,
|
||||||
},
|
},
|
||||||
|
|
@ -273,11 +259,7 @@ impl<'a> Step {
|
||||||
color_sliders,
|
color_sliders,
|
||||||
color,
|
color,
|
||||||
} => Self::text(size_slider, *size, color_sliders, *color).into(),
|
} => Self::text(size_slider, *size, color_sliders, *color).into(),
|
||||||
Step::Image {
|
Step::Image { width, slider } => Self::image(*width, slider).into(),
|
||||||
ferris,
|
|
||||||
width,
|
|
||||||
slider,
|
|
||||||
} => Self::image(ferris.clone(), *width, slider).into(),
|
|
||||||
Step::RowsAndColumns {
|
Step::RowsAndColumns {
|
||||||
layout,
|
layout,
|
||||||
spacing_slider,
|
spacing_slider,
|
||||||
|
|
@ -489,13 +471,16 @@ impl<'a> Step {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn image(
|
fn image(
|
||||||
ferris: graphics::Image,
|
|
||||||
width: u16,
|
width: u16,
|
||||||
slider: &'a mut slider::State,
|
slider: &'a mut slider::State,
|
||||||
) -> Column<'a, StepMessage> {
|
) -> Column<'a, StepMessage> {
|
||||||
Self::container("Image")
|
Self::container("Image")
|
||||||
.push(Text::new("An image that tries to keep its aspect ratio."))
|
.push(Text::new("An image that tries to keep its aspect ratio."))
|
||||||
.push(Image::new(ferris).width(width).align_self(Align::Center))
|
.push(
|
||||||
|
Image::new("resources/ferris.png")
|
||||||
|
.width(width)
|
||||||
|
.align_self(Align::Center),
|
||||||
|
)
|
||||||
.push(Slider::new(
|
.push(Slider::new(
|
||||||
slider,
|
slider,
|
||||||
100.0..=500.0,
|
100.0..=500.0,
|
||||||
|
|
|
||||||
16
web/src/color.rs
Normal file
16
web/src/color.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Color {
|
||||||
|
pub r: f32,
|
||||||
|
pub g: f32,
|
||||||
|
pub b: f32,
|
||||||
|
pub a: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
pub const BLACK: Color = Color {
|
||||||
|
r: 0.0,
|
||||||
|
g: 0.0,
|
||||||
|
b: 0.0,
|
||||||
|
a: 1.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
47
web/src/element.rs
Normal file
47
web/src/element.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
use crate::{Color, Widget};
|
||||||
|
|
||||||
|
pub struct Element<'a, Message> {
|
||||||
|
pub(crate) widget: Box<dyn Widget<Message> + 'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message> Element<'a, Message> {
|
||||||
|
pub fn new(widget: impl Widget<Message> + 'a) -> Self {
|
||||||
|
Self {
|
||||||
|
widget: Box::new(widget),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn explain(self, color: Color) -> Element<'a, Message> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<F, B>(self, f: F) -> Element<'a, B>
|
||||||
|
where
|
||||||
|
Message: 'static,
|
||||||
|
B: 'static,
|
||||||
|
F: 'static + Fn(Message) -> B,
|
||||||
|
{
|
||||||
|
Element {
|
||||||
|
widget: Box::new(Map::new(self.widget, f)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Map<'a, A, B> {
|
||||||
|
widget: Box<dyn Widget<A> + 'a>,
|
||||||
|
mapper: Box<dyn Fn(A) -> B>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, A, B> Map<'a, A, B> {
|
||||||
|
pub fn new<F>(widget: Box<dyn Widget<A> + 'a>, mapper: F) -> Map<'a, A, B>
|
||||||
|
where
|
||||||
|
F: 'static + Fn(A) -> B,
|
||||||
|
{
|
||||||
|
Map {
|
||||||
|
widget,
|
||||||
|
mapper: Box::new(mapper),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, A, B> Widget<B> for Map<'a, A, B> {}
|
||||||
|
|
@ -1 +1,28 @@
|
||||||
|
use futures::Future;
|
||||||
|
|
||||||
|
mod color;
|
||||||
|
mod element;
|
||||||
|
mod widget;
|
||||||
|
|
||||||
|
pub use color::Color;
|
||||||
|
pub use element::Element;
|
||||||
|
pub use iced::Align;
|
||||||
|
pub use widget::*;
|
||||||
|
|
||||||
|
pub trait UserInterface {
|
||||||
|
type Message;
|
||||||
|
|
||||||
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
message: Self::Message,
|
||||||
|
) -> Box<dyn Future<Item = Self::Message, Error = ()>>;
|
||||||
|
|
||||||
|
fn view(&mut self) -> Element<Self::Message>;
|
||||||
|
|
||||||
|
fn run(mut self)
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let element = self.view();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
20
web/src/widget.rs
Normal file
20
web/src/widget.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
pub mod button;
|
||||||
|
pub mod slider;
|
||||||
|
pub mod text;
|
||||||
|
|
||||||
|
mod checkbox;
|
||||||
|
mod column;
|
||||||
|
mod image;
|
||||||
|
mod radio;
|
||||||
|
mod row;
|
||||||
|
|
||||||
|
pub use button::Button;
|
||||||
|
pub use checkbox::Checkbox;
|
||||||
|
pub use column::Column;
|
||||||
|
pub use image::Image;
|
||||||
|
pub use radio::Radio;
|
||||||
|
pub use row::Row;
|
||||||
|
pub use slider::Slider;
|
||||||
|
pub use text::Text;
|
||||||
|
|
||||||
|
pub trait Widget<Message> {}
|
||||||
16
web/src/widget/button.rs
Normal file
16
web/src/widget/button.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::{Element, Widget};
|
||||||
|
|
||||||
|
pub use iced::button::{Class, State};
|
||||||
|
|
||||||
|
pub type Button<'a, Message> = iced::Button<'a, Message>;
|
||||||
|
|
||||||
|
impl<'a, Message> Widget<Message> for Button<'a, Message> {}
|
||||||
|
|
||||||
|
impl<'a, Message> From<Button<'a, Message>> for Element<'a, Message>
|
||||||
|
where
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
fn from(button: Button<'a, Message>) -> Element<'a, Message> {
|
||||||
|
Element::new(button)
|
||||||
|
}
|
||||||
|
}
|
||||||
14
web/src/widget/checkbox.rs
Normal file
14
web/src/widget/checkbox.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::{Color, Element, Widget};
|
||||||
|
|
||||||
|
pub type Checkbox<Message> = iced::Checkbox<Color, Message>;
|
||||||
|
|
||||||
|
impl<Message> Widget<Message> for Checkbox<Message> {}
|
||||||
|
|
||||||
|
impl<'a, Message> From<Checkbox<Message>> for Element<'a, Message>
|
||||||
|
where
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
fn from(checkbox: Checkbox<Message>) -> Element<'a, Message> {
|
||||||
|
Element::new(checkbox)
|
||||||
|
}
|
||||||
|
}
|
||||||
48
web/src/widget/column.rs
Normal file
48
web/src/widget/column.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use crate::{Align, Element, Widget};
|
||||||
|
|
||||||
|
pub struct Column<'a, Message> {
|
||||||
|
children: Vec<Element<'a, Message>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message> Column<'a, Message> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
children: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spacing(self, _spacing: u16) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn padding(self, _padding: u16) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn max_width(self, _max_width: u16) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn align_items(self, _align: Align) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push<E>(mut self, element: E) -> Self
|
||||||
|
where
|
||||||
|
E: Into<Element<'a, Message>>,
|
||||||
|
{
|
||||||
|
self.children.push(element.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message> Widget<Message> for Column<'a, Message> {}
|
||||||
|
|
||||||
|
impl<'a, Message> From<Column<'a, Message>> for Element<'a, Message>
|
||||||
|
where
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
fn from(column: Column<'a, Message>) -> Element<'a, Message> {
|
||||||
|
Element::new(column)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
web/src/widget/image.rs
Normal file
11
web/src/widget/image.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
use crate::{Element, Widget};
|
||||||
|
|
||||||
|
pub type Image<'a> = iced::Image<&'a str>;
|
||||||
|
|
||||||
|
impl<'a, Message> Widget<Message> for Image<'a> {}
|
||||||
|
|
||||||
|
impl<'a, Message> From<Image<'a>> for Element<'a, Message> {
|
||||||
|
fn from(image: Image<'a>) -> Element<'a, Message> {
|
||||||
|
Element::new(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
14
web/src/widget/radio.rs
Normal file
14
web/src/widget/radio.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::{Color, Element, Widget};
|
||||||
|
|
||||||
|
pub type Radio<Message> = iced::Radio<Color, Message>;
|
||||||
|
|
||||||
|
impl<Message> Widget<Message> for Radio<Message> {}
|
||||||
|
|
||||||
|
impl<'a, Message> From<Radio<Message>> for Element<'a, Message>
|
||||||
|
where
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
fn from(radio: Radio<Message>) -> Element<'a, Message> {
|
||||||
|
Element::new(radio)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
web/src/widget/row.rs
Normal file
36
web/src/widget/row.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use crate::{Element, Widget};
|
||||||
|
|
||||||
|
pub struct Row<'a, Message> {
|
||||||
|
children: Vec<Element<'a, Message>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message> Row<'a, Message> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
children: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spacing(self, _spacing: u16) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push<E>(mut self, element: E) -> Self
|
||||||
|
where
|
||||||
|
E: Into<Element<'a, Message>>,
|
||||||
|
{
|
||||||
|
self.children.push(element.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message> Widget<Message> for Row<'a, Message> {}
|
||||||
|
|
||||||
|
impl<'a, Message> From<Row<'a, Message>> for Element<'a, Message>
|
||||||
|
where
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
fn from(column: Row<'a, Message>) -> Element<'a, Message> {
|
||||||
|
Element::new(column)
|
||||||
|
}
|
||||||
|
}
|
||||||
16
web/src/widget/slider.rs
Normal file
16
web/src/widget/slider.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::{Element, Widget};
|
||||||
|
|
||||||
|
pub use iced::slider::State;
|
||||||
|
|
||||||
|
pub type Slider<'a, Message> = iced::Slider<'a, Message>;
|
||||||
|
|
||||||
|
impl<'a, Message> Widget<Message> for Slider<'a, Message> {}
|
||||||
|
|
||||||
|
impl<'a, Message> From<Slider<'a, Message>> for Element<'a, Message>
|
||||||
|
where
|
||||||
|
Message: 'static,
|
||||||
|
{
|
||||||
|
fn from(slider: Slider<'a, Message>) -> Element<'a, Message> {
|
||||||
|
Element::new(slider)
|
||||||
|
}
|
||||||
|
}
|
||||||
13
web/src/widget/text.rs
Normal file
13
web/src/widget/text.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
use crate::{Color, Element, Widget};
|
||||||
|
|
||||||
|
pub use iced::text::HorizontalAlignment;
|
||||||
|
|
||||||
|
pub type Text = iced::Text<Color>;
|
||||||
|
|
||||||
|
impl<'a, Message> Widget<Message> for Text {}
|
||||||
|
|
||||||
|
impl<'a, Message> From<Text> for Element<'a, Message> {
|
||||||
|
fn from(text: Text) -> Element<'a, Message> {
|
||||||
|
Element::new(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue