Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental composable widgets for styling #14

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod widget;
pub use app::App;
pub use app_main::AppLauncher;
pub use view::button::button;
pub use view::style::{background, padding};
pub use view::View;
pub use widget::align::VertAlignment;
pub use widget::Widget;
Expand Down
15 changes: 11 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
use xilem::{button, App, AppLauncher, View};
use piet_scene::Color;
use xilem::{background, button, padding, App, AppLauncher, View};

fn app_logic(_data: &mut ()) -> impl View<()> {
button("click me", |_| println!("clicked"))
fn app_logic(data: &mut f64) -> impl View<f64> {
background(
Color::RED,
padding(
*data,
button(format!("Padding {data}px"), |data| *data += 1.),
),
)
}

fn main() {
Expand All @@ -15,6 +22,6 @@ fn main() {
window_handle.show();
app.run(None);
*/
let app = App::new((), app_logic);
let app = App::new(10., app_logic);
AppLauncher::new(app).run()
}
1 change: 1 addition & 0 deletions src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod button;
// pub mod list;
// pub mod memoize;
// pub mod scroll_view;
pub mod style;
pub mod text;
// pub mod use_state;
// pub mod vstack;
Expand Down
150 changes: 150 additions & 0 deletions src/view/style.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use piet_scene::Color;

use crate::{
id::Id,
widget::{
compose_style::{background::BackgroundWidget, padding::PaddingWidget},
Pod,
},
View,
};

pub fn padding<V: View<T, A>, T, A>(width: f64, view: V) -> impl View<T, A>
where
V::Element: 'static,
{
PaddingView::new(width, view)
}

pub struct PaddingView<V> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an implicit decision about naming convention, that I really like;

It would be grammatically more obvious to name this PaddedView.

But I think this way it's way nicer, and we should adopt this as a naming convention.

E.g. BorderView instead of BorderedView etc.

This way it's a little easier to navigate the stuff xilem has to offer, even if your english is good. But more than anything, we make the library a little more accessible to non native speakers, who struggle with the language barrier

width: f64,
view: V,
}

impl<T, A, V: View<T, A>> View<T, A> for PaddingView<V>
where
V::Element: 'static,
{
type State = (Id, V::State);

type Element = PaddingWidget;

fn build(&self, cx: &mut crate::view::Cx) -> (crate::id::Id, Self::State, Self::Element) {
let (id, (child_id, state, element)) = cx.with_new_id(|cx| self.view.build(cx));
let element = PaddingWidget::new(Pod::new(element), self.width);
(id, (child_id, state), element)
}

fn rebuild(
&self,
cx: &mut crate::view::Cx,
prev: &Self,
id: &mut crate::id::Id,
(child_id, state): &mut Self::State,
element: &mut Self::Element,
) -> bool {
let mut changed = prev.width != self.width;
if changed {
element.set_width(self.width);
}
cx.with_id(*id, |cx| {
let child_element = element.widget.downcast_mut().unwrap();
let child_changed = self
.view
.rebuild(cx, &prev.view, child_id, state, child_element);
if child_changed {
changed = true;
element.widget.request_update();
}
});
changed
}

fn event(
&self,
id_path: &[crate::id::Id],
(child_id, state): &mut Self::State,
event: Box<dyn std::any::Any>,
app_state: &mut T,
) -> crate::event::EventResult<A> {
let (left, right) = id_path.split_at(1);
assert!(left[0] == *child_id);
self.view.event(right, state, event, app_state)
}
}

impl<V> PaddingView<V> {
fn new(width: f64, view: V) -> Self {
PaddingView { width, view }
}
}

pub fn background<V: View<T, A>, T, A>(color: Color, view: V) -> impl View<T, A>
where
V::Element: 'static,
{
BackgroundView::new(color, view)
}

pub struct BackgroundView<V> {
color: Color,
view: V,
}

impl<T, A, V: View<T, A>> View<T, A> for BackgroundView<V>
where
V::Element: 'static,
{
type State = (Id, V::State);

type Element = BackgroundWidget;

fn build(&self, cx: &mut crate::view::Cx) -> (crate::id::Id, Self::State, Self::Element) {
let (id, (child_id, state, element)) = cx.with_new_id(|cx| self.view.build(cx));
let element = BackgroundWidget::new(Pod::new(element), self.color);
(id, (child_id, state), element)
}

fn rebuild(
&self,
cx: &mut crate::view::Cx,
prev: &Self,
id: &mut crate::id::Id,
(child_id, state): &mut Self::State,
element: &mut Self::Element,
) -> bool {
let mut changed = prev.color != self.color;
if changed {
element.set_color(self.color);
}
cx.with_id(*id, |cx| {
let child_element = element.widget.downcast_mut().unwrap();
let child_changed = self
.view
.rebuild(cx, &prev.view, child_id, state, child_element);
if child_changed {
changed = true;
element.widget.request_update();
}
});
changed
}

fn event(
&self,
id_path: &[crate::id::Id],
(child_id, state): &mut Self::State,
event: Box<dyn std::any::Any>,
app_state: &mut T,
) -> crate::event::EventResult<A> {
let (left, right) = id_path.split_at(1);
assert!(left[0] == *child_id);
self.view.event(right, state, event, app_state)
}
}

impl<V> BackgroundView<V> {
fn new(color: Color, view: V) -> Self {
BackgroundView { color, view }
}
}
1 change: 1 addition & 0 deletions src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod core;
pub mod piet_scene_helpers;
mod raw_event;
//pub mod scroll_view;
pub mod compose_style;
pub mod text;
//pub mod vstack;

Expand Down
2 changes: 2 additions & 0 deletions src/widget/compose_style.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod background;
pub mod padding;
64 changes: 64 additions & 0 deletions src/widget/compose_style/background.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use glazier::kurbo::Affine;
use piet_scene::{Color, Fill};

use crate::{id::Id, widget::Pod, View, Widget};

pub struct BackgroundWidget {
pub(crate) widget: Pod,
color: Color,
}

impl BackgroundWidget {
pub fn new(widget: Pod, color: Color) -> Self {
Self { widget, color }
}
pub(crate) fn set_color(&mut self, color: Color) {
self.color = color;
}
}

impl Widget for BackgroundWidget {
fn event(&mut self, cx: &mut crate::widget::EventCx, event: &crate::widget::RawEvent) {
self.widget.event(cx, event)
}

fn lifecycle(
&mut self,
cx: &mut crate::widget::contexts::LifeCycleCx,
event: &crate::widget::LifeCycle,
) {
self.widget.lifecycle(cx, event)
}

fn update(&mut self, cx: &mut crate::widget::UpdateCx) {
self.widget.update(cx)
}

fn measure(
&mut self,
cx: &mut crate::widget::LayoutCx,
) -> (glazier::kurbo::Size, glazier::kurbo::Size) {
self.widget.measure(cx)
}

fn layout(
&mut self,
cx: &mut crate::widget::LayoutCx,
proposed_size: glazier::kurbo::Size,
) -> glazier::kurbo::Size {
self.widget.layout(cx, proposed_size)
}

fn paint(&mut self, cx: &mut crate::widget::PaintCx, builder: &mut piet_scene::SceneBuilder) {
self.widget.paint(cx);
let fragment = self.widget.fragment();
builder.fill(
Fill::NonZero,
Affine::IDENTITY,
self.color,
None,
&cx.size().to_rect(),
);
builder.append(fragment, None)
}
}
Loading