Skip to content
Snippets Groups Projects
Commit e65f9ce9 authored by StarArawn's avatar StarArawn
Browse files

Fully working todo..

parent e1073be4
No related branches found
No related tags found
No related merge requests found
Showing with 393 additions and 20 deletions
......@@ -27,3 +27,7 @@ kayak_render_macros = { path = "kayak_render_macros" }
[dev-dependencies]
bevy = { git = "https://github.com/bevyengine/bevy", rev = "9a16a4d01830297987db40b45f03382ed3acad62" }
kayak_widgets = { path = "kayak_widgets", features = ["bevy_renderer"] }
[[example]]
name = "todo"
path = "examples/todo/todo.rs"
......@@ -78,6 +78,8 @@ pub fn extract(
vec![]
};
// dbg!(&render_primitives);
let dpi = if let Some(window) = windows.get_primary() {
window.scale_factor() as f32
} else {
......
......@@ -38,8 +38,8 @@ fn TextBoxExample(context: &mut KayakContext) {
rsx! {
<>
<Window position={(50.0, 50.0)} size={(300.0, 300.0)} title={"TextBox Example".to_string()}>
<TextBox styles={Some(input_styles)} value={current_value} on_change={Some(on_change)} />
<TextBox styles={Some(input_styles)} value={current_value2} on_change={Some(on_change2)} />
<TextBox styles={Some(input_styles)} value={current_value} on_change={Some(on_change)} placeholder={None as Option<String>} />
<TextBox styles={Some(input_styles)} value={current_value2} on_change={Some(on_change2)} placeholder={None as Option<String>} />
</Window>
</>
}
......
use kayak_ui::core::{
color::Color,
render_command::RenderCommand,
rsx,
styles::{Style, StyleProp, Units},
use_state, widget, Bound, Children, EventType, MutableBound, OnEvent,
};
use kayak_widgets::{Background, Text};
#[widget]
pub fn AddButton(children: Children, styles: Option<Style>) {
let (color, set_color) = use_state!(Color::new(0.0781, 0.0898, 0.101, 1.0));
let base_styles = styles.clone().unwrap_or_default();
*styles = Some(Style {
render_command: StyleProp::Value(RenderCommand::Layout),
height: StyleProp::Value(Units::Pixels(32.0)),
width: StyleProp::Value(Units::Pixels(30.0)),
..base_styles
});
let background_styles = Some(Style {
border_radius: StyleProp::Value((5.0, 5.0, 5.0, 5.0)),
background_color: StyleProp::Value(color),
padding_left: StyleProp::Value(Units::Pixels(9.0)),
padding_bottom: StyleProp::Value(Units::Pixels(6.0)),
..Style::default()
});
let on_event = OnEvent::new(move |_, event| match event.event_type {
EventType::MouseIn => {
set_color(Color::new(0.0791, 0.0998, 0.201, 1.0));
}
EventType::MouseOut => {
set_color(Color::new(0.0781, 0.0898, 0.101, 1.0));
}
_ => {}
});
rsx! {
<Background styles={background_styles} on_event={Some(on_event)}>
<Text content={"+".to_string()} size={20.0} />
</Background>
}
}
use kayak_core::{
rsx,
styles::{LayoutType, Style, StyleProp, Units},
widget, Color, EventType, Handler, OnEvent,
};
use kayak_widgets::{Background, Text};
use super::delete_button::DeleteButton;
#[widget]
pub fn Card(card_id: usize, name: String, on_delete: Handler<usize>) {
let name = name.clone();
let background_styles = Style {
layout_type: StyleProp::Value(LayoutType::Row),
background_color: StyleProp::Value(Color::new(0.176, 0.196, 0.215, 1.0)),
height: StyleProp::Value(Units::Pixels(26.0)),
top: StyleProp::Value(Units::Pixels(10.0)),
padding_left: StyleProp::Value(Units::Pixels(5.0)),
padding_right: StyleProp::Value(Units::Pixels(5.0)),
padding_top: StyleProp::Value(Units::Pixels(5.0)),
padding_bottom: StyleProp::Value(Units::Pixels(5.0)),
..Style::default()
};
let on_delete = on_delete.clone();
let card_id = *card_id;
let on_event = OnEvent::new(move |_, event| match event.event_type {
EventType::Click => {
on_delete.call(card_id);
}
_ => (),
});
rsx! {
<Background styles={Some(background_styles)}>
<Text size={14.0} content={name} />
<DeleteButton on_event={Some(on_event)} />
</Background>
}
}
use kayak_core::{constructor, rsx, widget, Handler, VecTracker};
use kayak_widgets::Element;
use super::{card::Card, Todo};
#[widget]
pub fn Cards(cards: Vec<Todo>, on_delete: Handler<usize>) {
let cards = cards.clone();
let on_delete = on_delete.clone();
rsx! {
<Element>
{VecTracker::from(
cards
.clone()
.into_iter()
.enumerate()
.map(|(index, todo)| constructor! { <Card card_id={index} name={todo.name.clone()} on_delete={on_delete.clone()} /> }),
)}
</Element>
}
}
use kayak_ui::core::{
color::Color,
render_command::RenderCommand,
rsx,
styles::{Style, StyleProp, Units},
use_state, widget, Bound, Children, EventType, MutableBound, OnEvent,
};
use kayak_widgets::{Background, Text};
#[widget]
pub fn DeleteButton(children: Children, styles: Option<Style>) {
let (color, set_color) = use_state!(Color::new(0.0781, 0.0898, 0.101, 1.0));
let base_styles = styles.clone().unwrap_or_default();
*styles = Some(Style {
render_command: StyleProp::Value(RenderCommand::Layout),
height: StyleProp::Value(Units::Pixels(32.0)),
width: StyleProp::Value(Units::Pixels(30.0)),
..base_styles
});
let background_styles = Some(Style {
border_radius: StyleProp::Value((5.0, 5.0, 5.0, 5.0)),
background_color: StyleProp::Value(color),
padding_left: StyleProp::Value(Units::Pixels(8.0)),
..Style::default()
});
let on_event = OnEvent::new(move |_, event| match event.event_type {
EventType::MouseIn => {
set_color(Color::new(0.0791, 0.0998, 0.201, 1.0));
}
EventType::MouseOut => {
set_color(Color::new(0.0781, 0.0898, 0.101, 1.0));
}
_ => {}
});
rsx! {
<Background styles={background_styles} on_event={Some(on_event)}>
<Text content={"X".to_string()} size={20.0} />
</Background>
}
}
use bevy::{
prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut},
window::WindowDescriptor,
DefaultPlugins,
};
use kayak_ui::bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
use kayak_ui::core::{
render, rsx,
styles::{LayoutType, Style, StyleProp, Units},
use_state, widget, Bound, EventType, Handler, Index, MutableBound, OnEvent,
};
use kayak_widgets::{App, Element, OnChange, TextBox, Window};
mod add_button;
mod card;
mod cards;
mod delete_button;
use add_button::AddButton;
use cards::Cards;
#[derive(Debug, Clone, PartialEq)]
pub struct Todo {
name: String,
}
#[widget]
fn TodoApp() {
let (todos, set_todos) = use_state!(vec![
Todo {
name: "Use bevy to make a game!".to_string(),
},
Todo {
name: "Help contribute to bevy!".to_string(),
},
Todo {
name: "Join the bevy discord!".to_string(),
},
]);
let (new_todo_value, set_new_todo_value) = use_state!("".to_string());
let text_box_styles = Style {
right: StyleProp::Value(Units::Pixels(10.0)),
..Style::default()
};
let top_area_styles = Style {
layout_type: StyleProp::Value(LayoutType::Row),
bottom: StyleProp::Value(Units::Pixels(10.0)),
height: StyleProp::Value(Units::Pixels(30.0)),
padding_top: StyleProp::Value(Units::Stretch(1.0)),
padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
..Style::default()
};
let on_change = OnChange::new(move |event| {
set_new_todo_value(event.value);
});
let new_todo_value_cloned = new_todo_value.clone();
let mut todos_cloned = todos.clone();
let cloned_set_todos = set_todos.clone();
let add_events = OnEvent::new(move |_, event| match event.event_type {
EventType::Click => {
if !new_todo_value_cloned.is_empty() {
todos_cloned.push(Todo {
name: new_todo_value_cloned.clone(),
});
cloned_set_todos(todos_cloned.clone());
}
}
_ => {}
});
let mut todos_cloned = todos.clone();
let cloned_set_todos = set_todos.clone();
let handle_delete = Handler::new(move |card_id: usize| {
todos_cloned.remove(card_id);
cloned_set_todos(todos_cloned.clone());
});
let placeholder = Some("Type here to add a new todo!".to_string());
rsx! {
<Window position={(415.0, 50.0)} size={(450.0, 600.0)} title={"Todo!".to_string()}>
<Element styles={Some(top_area_styles)}>
<TextBox styles={Some(text_box_styles)} value={new_todo_value} placeholder={placeholder} on_change={Some(on_change)} />
<AddButton on_event={Some(add_events)} />
</Element>
<Cards cards={todos} on_delete={handle_delete} />
</Window>
}
}
fn startup(
mut commands: Commands,
mut font_mapping: ResMut<FontMapping>,
asset_server: Res<AssetServer>,
) {
commands.spawn_bundle(UICameraBundle::new());
font_mapping.add(asset_server.load("roboto.kayak_font"));
let context = BevyContext::new(|context| {
render! {
<App>
<TodoApp />
</App>
}
});
commands.insert_resource(context);
}
fn main() {
BevyApp::new()
.insert_resource(WindowDescriptor {
width: 1270.0,
height: 720.0,
title: String::from("UI Example"),
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugin(BevyKayakUIPlugin)
.add_startup_system(startup)
.run();
}
......@@ -32,6 +32,9 @@ pub use resources::Resources;
pub use tree::{Tree, WidgetTree};
pub use vec::VecTracker;
pub use widget::Widget;
pub mod derivative {
pub use derivative::*;
}
pub type Children = Option<
Arc<dyn Fn(WidgetTree, Option<crate::Index>, &mut crate::context::KayakContext) + Send + Sync>,
......@@ -52,6 +55,29 @@ impl OnEvent {
}
}
pub mod derivative {
pub use derivative::*;
#[derive(Clone)]
pub struct Handler<T>(pub Arc<RwLock<dyn FnMut(T) + Send + Sync + 'static>>);
impl<T> Handler<T> {
pub fn new<F: FnMut(T) + Send + Sync + 'static>(f: F) -> Handler<T> {
Handler(Arc::new(RwLock::new(f)))
}
pub fn call(&self, data: T) {
if let Ok(mut handler) = self.0.write() {
handler(data);
}
}
}
impl<T> PartialEq for Handler<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T> std::fmt::Debug for Handler<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Handler").finish()
}
}
......@@ -116,11 +116,29 @@ impl<'a> morphorm::Node<'a> for Index {
return Some(morphorm::Units::Stretch(1.0));
}
fn min_width(&self, _store: &'_ Self::Data) -> Option<morphorm::Units> {
fn min_width(&self, store: &'_ Self::Data) -> Option<morphorm::Units> {
if let Some(node) = store.get(*self) {
if let Some(node) = node {
return match node.styles.min_width {
StyleProp::Default => Some(morphorm::Units::Stretch(1.0)),
StyleProp::Value(prop) => Some(prop),
_ => Some(morphorm::Units::Auto),
};
}
}
Some(morphorm::Units::Auto)
}
fn min_height(&self, _store: &'_ Self::Data) -> Option<morphorm::Units> {
fn min_height(&self, store: &'_ Self::Data) -> Option<morphorm::Units> {
if let Some(node) = store.get(*self) {
if let Some(node) = node {
return match node.styles.min_height {
StyleProp::Default => Some(morphorm::Units::Stretch(1.0)),
StyleProp::Value(prop) => Some(prop),
_ => Some(morphorm::Units::Auto),
};
}
}
Some(morphorm::Units::Auto)
}
......
......@@ -53,6 +53,8 @@ pub struct Style {
pub margin_right: StyleProp<Units>,
pub margin_top: StyleProp<Units>,
pub margin_bottom: StyleProp<Units>,
pub min_width: StyleProp<Units>,
pub min_height: StyleProp<Units>,
}
impl Default for Style {
......@@ -78,6 +80,8 @@ impl Default for Style {
margin_right: StyleProp::Default,
margin_top: StyleProp::Default,
margin_bottom: StyleProp::Default,
min_width: StyleProp::Default,
min_height: StyleProp::Default,
}
}
}
......
......@@ -3,7 +3,7 @@ use derivative::*;
use crate::{context::KayakContext, styles::Style, Index, Widget};
#[derive(Derivative)]
#[derivative(Debug, PartialEq)]
#[derivative(Debug, PartialEq, Clone)]
pub struct VecTracker<T> {
pub id: Index,
pub styles: Option<Style>,
......
......@@ -256,6 +256,7 @@ impl WidgetManager {
nodes: &Arena<Option<Node>>,
current_node: Index,
mut main_z_index: f32,
mut prev_clip: RenderPrimitive,
) -> Vec<RenderPrimitive> {
let mut render_primitives = Vec::new();
......@@ -272,6 +273,14 @@ impl WidgetManager {
render_primitive.set_layout(layout);
render_primitives.push(render_primitive.clone());
let new_prev_clip = if matches!(render_primitive, RenderPrimitive::Clip { .. }) {
render_primitive.clone()
} else {
prev_clip
};
prev_clip = new_prev_clip.clone();
if node_tree.children.contains_key(&current_node) {
for child in node_tree.children.get(&current_node).unwrap() {
main_z_index += 1.0;
......@@ -281,22 +290,36 @@ impl WidgetManager {
nodes,
*child,
main_z_index,
new_prev_clip.clone(),
));
main_z_index = layout.z_index;
// Between each child node we need to reset the clip.
if matches!(render_primitive, RenderPrimitive::Clip { .. }) {
main_z_index = new_z_index;
match &mut render_primitive {
if matches!(prev_clip, RenderPrimitive::Clip { .. }) {
// main_z_index = new_z_index;
match &mut prev_clip {
RenderPrimitive::Clip { layout } => {
layout.z_index = main_z_index + 0.1;
}
_ => {}
};
render_primitives.push(render_primitive.clone());
render_primitives.push(prev_clip.clone());
}
}
}
// if matches!(render_primitive, RenderPrimitive::Clip { .. })
// && matches!(prev_clip, RenderPrimitive::Clip { .. })
// {
// // main_z_index = new_z_index;
// match &mut prev_clip {
// RenderPrimitive::Clip { layout } => {
// layout.z_index = main_z_index + 0.1;
// }
// _ => {}
// };
// render_primitives.push(render_primitive.clone());
// }
}
}
......@@ -310,6 +333,7 @@ impl WidgetManager {
&self.nodes,
self.node_tree.root_node.unwrap(),
0.0,
RenderPrimitive::Empty,
)
}
......
......@@ -104,7 +104,6 @@ pub fn dyn_partial_eq(_: TokenStream, input: TokenStream) -> TokenStream {
.into()
}
/// Create a state and its setter
///
/// # Arguments
......@@ -151,4 +150,4 @@ pub fn use_state(initial_state: TokenStream) -> TokenStream {
(state.get(), set_state)
}};
TokenStream::from(result)
}
\ No newline at end of file
}
use kayak_ui::core::{rsx, widget, Children};
use kayak_ui::core::{
render_command::RenderCommand,
rsx,
styles::{Style, StyleProp},
widget, Children,
};
#[widget]
pub fn Element(children: Children) {
*styles = Some(Style {
render_command: StyleProp::Value(RenderCommand::Layout),
..styles.clone().unwrap_or_default()
});
rsx! {
<>
{children}
......
......@@ -38,9 +38,12 @@ impl std::fmt::Debug for OnChange {
pub struct Focus(pub bool);
#[widget(focusable)]
pub fn TextBox(value: String, on_change: Option<OnChange>) {
pub fn TextBox(value: String, on_change: Option<OnChange>, placeholder: Option<String>) {
*styles = Some(Style {
render_command: StyleProp::Value(RenderCommand::Layout),
height: StyleProp::Value(Units::Pixels(26.0)),
top: StyleProp::Value(Units::Pixels(0.0)),
bottom: StyleProp::Value(Units::Pixels(0.0)),
..styles.clone().unwrap_or_default()
});
......@@ -83,7 +86,11 @@ pub fn TextBox(value: String, on_change: Option<OnChange>) {
_ => {}
}));
let value = value.clone();
let value = if value.is_empty() {
placeholder.clone().unwrap_or(value.clone())
} else {
value.clone()
};
rsx! {
<Background styles={Some(background_styles)}>
<Clip>
......
......@@ -56,10 +56,10 @@ pub fn Window(
};
let content_styles = Style {
padding_left: StyleProp::Value(Units::Stretch(1.0)),
padding_right: StyleProp::Value(Units::Stretch(1.0)),
padding_top: StyleProp::Value(Units::Stretch(1.0)),
padding_bottom: StyleProp::Value(Units::Stretch(1.0)),
padding_left: StyleProp::Value(Units::Pixels(10.0)),
padding_right: StyleProp::Value(Units::Pixels(10.0)),
padding_top: StyleProp::Value(Units::Pixels(10.0)),
padding_bottom: StyleProp::Value(Units::Pixels(10.0)),
..Style::default()
};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment