-
Notifications
You must be signed in to change notification settings - Fork 50
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
Please add ability to serialize/deserialize from structured file format such as JSON or YAML #170
Comments
How about modern/readable format like KDL? It fits pretty well suited for structured UI with e.g. CSS styling. |
Sure, I will look into it. This is what I've got so far: Parser use serde_json;
use std::{result::Result};
use serde::{Deserialize, Serialize};
use crate::morph::{build_text_widget, UiNode};
// use crate::morph::build_world;
type OptStr = Option<String>;
// type OptNum = Option<u32>;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UiParseNode {
#[serde(skip_serializing_if = "Option::is_none")]
pub text: OptStr,
pub width: OptStr,
pub height: OptStr,
pub child_space: OptStr,
pub position_type: OptStr,
}
pub fn parse_ui() -> Result<UiNode, &'static str> {
let ui_json = r#"
{
"width": "2 px",
"height": "5 px"
}
"#;
let ui = serde_json::from_str(ui_json).unwrap();
println!("ui {:#?}", ui);
build_text_widget(ui)
} Builder use std::{any::Any};
use kayak_ui::{widgets::{TextWidgetBundle, TextProps}};
use regex::Regex;
use crate::morph_serde::{UiParseNode, OptStr}; // use morphorm::Cache;
trait UiParser {
fn parse(&self) -> Result<Box<dyn Any>, &'static str>;
}
struct UiTextProps {
node: UiParseNode
}
impl UiTextProps {
fn new(node: UiParseNode) -> Self {
Self {
node
}
}
}
impl UiParser for UiTextProps {
fn parse(&self) -> Result<Box<dyn Any>, &'static str> {
let text = &self.node.text.clone();
let content = text.to_owned().unwrap_or("".to_string());
let widget = TextProps {
content: content.to_string(),
..Default::default()
};
Ok(Box::new(widget))
}
}
struct UiTextWidget {
node: UiParseNode
}
impl UiParser for UiTextWidget {
fn parse(&self) -> Result<Box<dyn Any>, &'static str> {
let text = UiTextProps::new(self.node.to_owned()).parse()?;
if let Ok(content) = text.downcast::<TextProps>() {
let widget = TextWidgetBundle {
text: *content,
..Default::default()
};
Ok(Box::new(widget))
} else {
Err("bad TextProps")
}
}
}
pub struct UiNode {
unit: UiNodeUnit
}
impl UiNode {
fn new(unit: UiNodeUnit) -> Self {
Self {
unit
}
}
}
pub enum UiNodeUnit {
Pixels(f32),
}
pub fn build_text_widget(ui: UiParseNode) -> Result<UiNode, &'static str> {
if let Ok(unit) = parse_unit(ui.width) {
Ok(UiNode::new(unit))
} else {
Err("bad Text Widget")
}
}
pub fn parse_unit(optstr: OptStr) -> Result<UiNodeUnit, &'static str> {
if let Some(str) = optstr {
let re = Regex::new(r"px\s*$").unwrap();
let str = re.replace(&str, "");
let number = &str[..str.len() - 2];
let pixels = number.parse::<f32>().unwrap();
Ok(UiNodeUnit::Pixels(pixels))
} else {
Err("bad unit")
}
} |
Note: I'm only starting to learn Rust so I'm sure the above code could be much improved. You're welcome to make suggestions :) |
kayak_ui uses nanoserde instead of serde internally for msdf data and fast compile times. |
I think that some internal structs should likely implement nanoserde traits or regular serde traits hidden behind feature flags. As for building UI's using JSON, YAML, or KDL I'm not convinced that those formats would be beneficial. @MrGVSV and I had an idea where we could write widgets as scripts in a scripting language so that widgets could be modified at runtime. I think is probably an ideal path forward here to having asset loaded widgets. As an intermediate step I do want to be able to define styles as assets so look for that change in the near future. :) |
@StarArawn Thanks for your suggestions and feedback. I'm just playing around and experimenting for now (leveling up on Rust). My initial idea is be to load certain Widget structs from a structured file format (such as JSON) into a |
Working on it here: https://github.com/kristianmandrup/kayak_ui_deserializer |
Looking at nanoserde as suggested. JSON proposal below.
{
"assets": {
"images": [
{
"name": "profile-image",
"type": "image",
"path": "path/to/profile.png"
}
],
"fonts": [
{
"name": "roboto",
"type": "font",
"path": "path/to/roboto.tff"
}
]
},
"styles": [
{
"name": "base",
"color": "white",
"background-color": "darkgray"
},
{
"name": "base-image",
"border-radius": "500",
"position-type": "self-directed"
}
],
"widgets": {
"buttons": [
{
"name": "menu-button",
"type": "button",
"style": {
"extends": "base",
"bottom": "20 px",
"cursor": "hand"
}
}
],
"text-widgets": [
{
"name": "game-title",
"type": "text-widget",
"text": {
"extends": "base",
"content": "hello",
"size": 20,
"font-ref": "roboto"
}
}
],
"image-bundles": [
{
"name": "my-image",
"type": "image-bundle",
"image-ref": "profile-image",
"styles": {
"extends": "base-image",
"left": "10 px",
"top": "10 px",
"width": "200 px",
"height": "182 px"
}
}
]
}
} |
I've now completed first rough implementation here: https://github.com/kristianmandrup/kayak_ui_deserializer Only just started on my Rust journey a couple of months ago in order to get into Game Dev, so I'd love for anyone to make suggestions for improvements or try it out. Cheers. PS: This issue can be either closed or marked as feature suggestion (for progress tracking) as you prefer. |
It would be really cool to be able to write the Kayak UI in an external file such as a JSON file, similar to a CSS file and have it loaded and the Kayak UI built (builder pattern), similar to how the
rsx!
macro works.I would love to help with this effort if you can help get me started and point me in the right direction. I first thought there was a general
Widget
struct that could be returned for each Widget parser, but it looks like I would need to resort toBox<dyn Any>
for a more generic approach?The text was updated successfully, but these errors were encountered: