Nicer rust code
This commit is contained in:
parent
2e2e7a7839
commit
6755a90734
7 changed files with 153 additions and 53 deletions
|
@ -19,3 +19,8 @@ This is similar to Dear ImGui but separates the layout from the rendering, and a
|
||||||
Input is gathered in TypeScript.
|
Input is gathered in TypeScript.
|
||||||
PaintCommands rendered to a HTML canvas.
|
PaintCommands rendered to a HTML canvas.
|
||||||
Everything else is written in Rust, compiled to WASM.
|
Everything else is written in Rust, compiled to WASM.
|
||||||
|
|
||||||
|
# Test goal:
|
||||||
|
|
||||||
|
Make an "any" editor. Store text files, make a VERY SIMPLE text editor, in the web.
|
||||||
|
Supports MARKDEEP. A place for you ideas. Stored on your computer (local storage).
|
||||||
|
|
24
build.sh
24
build.sh
|
@ -15,19 +15,23 @@ rm -rf docs/*.d.ts
|
||||||
rm -rf docs/*.js
|
rm -rf docs/*.js
|
||||||
rm -rf docs/*.wasm
|
rm -rf docs/*.wasm
|
||||||
|
|
||||||
echo "Build rust:"
|
function build_rust
|
||||||
cargo build --target wasm32-unknown-unknown
|
{
|
||||||
|
echo "Build rust:"
|
||||||
|
cargo build --target wasm32-unknown-unknown
|
||||||
|
|
||||||
echo "Lint and clean up typescript:"
|
echo "Generate JS bindings for wasm:"
|
||||||
tslint --fix docs/*.ts
|
FOLDER_NAME=${PWD##*/}
|
||||||
|
TARGET_NAME="$FOLDER_NAME.wasm"
|
||||||
|
wasm-bindgen "target/wasm32-unknown-unknown/$BUILD/$TARGET_NAME" \
|
||||||
|
--out-dir docs --no-modules
|
||||||
|
# --no-modules-global hoboho
|
||||||
|
}
|
||||||
|
|
||||||
echo "Compile typescript:"
|
echo "Compile typescript:"
|
||||||
|
build_rust
|
||||||
tsc
|
tsc
|
||||||
|
|
||||||
echo "Generate JS bindings for wasm:"
|
# wait || exit $?
|
||||||
|
|
||||||
FOLDER_NAME=${PWD##*/}
|
# 3.4 s
|
||||||
TARGET_NAME="$FOLDER_NAME.wasm"
|
|
||||||
wasm-bindgen "target/wasm32-unknown-unknown/$BUILD/$TARGET_NAME" \
|
|
||||||
--out-dir docs --no-modules
|
|
||||||
# --no-modules-global hoboho
|
|
||||||
|
|
Binary file not shown.
|
@ -2,7 +2,7 @@
|
||||||
<html>
|
<html>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<head>
|
<head>
|
||||||
<title>Gui Experiment</title>
|
<title>Emgui – A experiment in an Immediate Mode GUI written in Rust</title>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
html {
|
html {
|
||||||
|
@ -11,9 +11,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: #111111;
|
background: #000000;
|
||||||
color: #bbbbbb;
|
color: #bbbbbb;
|
||||||
max-width: 480px;
|
max-width: 1024px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
<script src="frontend.js" type="module"></script>
|
<script src="frontend.js" type="module"></script>
|
||||||
|
|
||||||
<!-- TODO: make this cover the entire screen, with resize and all -->
|
<!-- TODO: make this cover the entire screen, with resize and all -->
|
||||||
<canvas id="canvas" width="480" height="800"></canvas>
|
<canvas id="canvas" width="1024" height="768"></canvas>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
5
lint.sh
Executable file
5
lint.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
echo "Lint and clean up typescript:"
|
||||||
|
tslint --fix docs/*.ts
|
152
src/lib.rs
152
src/lib.rs
|
@ -15,6 +15,76 @@ mod types;
|
||||||
|
|
||||||
use types::*;
|
use types::*;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
// Fast compilation, slow code:
|
||||||
|
fn foo(x: &dyn Trait);
|
||||||
|
|
||||||
|
|
||||||
|
// Fast code, slow compilation:
|
||||||
|
fn foo<T: Trait>(x: &dyn T);
|
||||||
|
|
||||||
|
|
||||||
|
// Compiles quickly in debug, fast in release:
|
||||||
|
#[dynimp(Trait)]
|
||||||
|
fn foo(x: &Trait);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct InteractInfo {
|
||||||
|
pub is_hovering: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: implement Gui on this so we can add children to a widget
|
||||||
|
// pub struct Widget {}
|
||||||
|
|
||||||
|
pub struct Gui {
|
||||||
|
commands: Vec<PaintCmd>,
|
||||||
|
input: Input,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gui {
|
||||||
|
pub fn new(input: Input) -> Self {
|
||||||
|
Gui {
|
||||||
|
commands: vec![PaintCmd::Clear {
|
||||||
|
fill_style: "#44444400".to_string(),
|
||||||
|
}],
|
||||||
|
input,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn input(&self) -> &Input {
|
||||||
|
&self.input
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_commands(self) -> Vec<PaintCmd> {
|
||||||
|
self.commands
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rect(&mut self, rect: Rect) -> InteractInfo {
|
||||||
|
let ii = InteractInfo {
|
||||||
|
is_hovering: rect.contains(&self.input.mouse_pos),
|
||||||
|
};
|
||||||
|
self.commands.push(PaintCmd::RoundedRect {
|
||||||
|
fill_style: "#ffffff10".to_string(),
|
||||||
|
pos: rect.pos,
|
||||||
|
corner_radius: 40.0,
|
||||||
|
size: rect.size,
|
||||||
|
});
|
||||||
|
ii
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text(&mut self, pos: Vec2, text: String) {
|
||||||
|
self.commands.push(PaintCmd::Text {
|
||||||
|
fill_style: "#11ff00".to_string(),
|
||||||
|
font: "14px Palatino".to_string(),
|
||||||
|
pos,
|
||||||
|
text,
|
||||||
|
text_align: TextAlign::Start,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct App {
|
struct App {
|
||||||
count: i32,
|
count: i32,
|
||||||
}
|
}
|
||||||
|
@ -24,42 +94,55 @@ impl App {
|
||||||
App { count: 0 }
|
App { count: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_gui(&mut self, input: &Input) -> Vec<PaintCmd> {
|
fn show_gui(&mut self, gui: &mut Gui, input: &Input) {
|
||||||
let rect = Rect {
|
gui.rect(Rect {
|
||||||
pos: Vec2 { x: 100.0, y: 100.0 },
|
pos: Vec2 { x: 0.0, y: 0.0 },
|
||||||
size: Vec2 { x: 200.0, y: 200.0 },
|
size: input.screen_size,
|
||||||
};
|
});
|
||||||
|
|
||||||
let is_hovering = rect.contains(&input.mouse_pos);
|
gui.rect(Rect {
|
||||||
|
pos: Vec2 { x: 50.0, y: 50.0 },
|
||||||
|
size: Vec2 {
|
||||||
|
x: (input.screen_size.x - 100.0) / 3.0,
|
||||||
|
y: (input.screen_size.y - 100.0),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
vec![
|
let is_hovering = gui
|
||||||
PaintCmd::Clear {
|
.rect(Rect {
|
||||||
fill_style: "#44444400".to_string(),
|
pos: Vec2 { x: 100.0, y: 100.0 },
|
||||||
|
size: Vec2 { x: 200.0, y: 200.0 },
|
||||||
|
}).is_hovering;
|
||||||
|
|
||||||
|
if is_hovering {
|
||||||
|
self.count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.text(
|
||||||
|
Vec2 { x: 100.0, y: 350.0 },
|
||||||
|
format!(
|
||||||
|
"Mouse pos: {} {}, is_hovering: {}",
|
||||||
|
input.mouse_pos.x, input.mouse_pos.y, is_hovering
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let m = input.mouse_pos;
|
||||||
|
let hw = 32.0;
|
||||||
|
gui.rect(Rect {
|
||||||
|
pos: Vec2 {
|
||||||
|
x: m.x - hw,
|
||||||
|
y: m.y - hw,
|
||||||
},
|
},
|
||||||
PaintCmd::Text {
|
size: Vec2 {
|
||||||
fill_style: "#11ff00".to_string(),
|
x: 2.0 * hw,
|
||||||
font: "14px Palatino".to_string(),
|
y: 2.0 * hw,
|
||||||
pos: Vec2 { x: 200.0, y: 32.0 },
|
|
||||||
text: format!(
|
|
||||||
"Mouse pos: {} {}, is_hovering: {}",
|
|
||||||
input.mouse_pos.x, input.mouse_pos.y, is_hovering
|
|
||||||
),
|
|
||||||
text_align: TextAlign::Center,
|
|
||||||
},
|
},
|
||||||
PaintCmd::Text {
|
});
|
||||||
fill_style: "#11ff00".to_string(),
|
|
||||||
font: "14px Palatino".to_string(),
|
gui.text(
|
||||||
pos: Vec2 { x: 200.0, y: 64.0 },
|
Vec2 { x: 100.0, y: 400.0 },
|
||||||
text: format!("Count: {}", self.count),
|
format!("Count: {}", self.count),
|
||||||
text_align: TextAlign::Center,
|
);
|
||||||
},
|
|
||||||
PaintCmd::RoundedRect {
|
|
||||||
fill_style: "#1111ff".to_string(),
|
|
||||||
pos: rect.pos,
|
|
||||||
corner_radius: 40.0,
|
|
||||||
size: rect.size,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +154,9 @@ pub fn show_gui(input_json: &str) -> String {
|
||||||
|
|
||||||
// TODO: faster interface than JSON
|
// TODO: faster interface than JSON
|
||||||
let input: Input = serde_json::from_str(input_json).unwrap();
|
let input: Input = serde_json::from_str(input_json).unwrap();
|
||||||
let commands = APP.lock().unwrap().show_gui(&input);
|
|
||||||
|
let mut gui = Gui::new(input);
|
||||||
|
APP.lock().unwrap().show_gui(&mut gui, &input);
|
||||||
|
let commands = gui.into_commands();
|
||||||
serde_json::to_string(&commands).unwrap()
|
serde_json::to_string(&commands).unwrap()
|
||||||
}
|
}
|
||||||
|
|
12
src/types.rs
12
src/types.rs
|
@ -1,10 +1,10 @@
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||||
pub struct Vec2 {
|
pub struct Vec2 {
|
||||||
pub x: f32,
|
pub x: f32,
|
||||||
pub y: f32,
|
pub y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
pub pos: Vec2,
|
pub pos: Vec2,
|
||||||
pub size: Vec2,
|
pub size: Vec2,
|
||||||
|
@ -19,21 +19,21 @@ impl Rect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Clone, Copy, Debug, Deserialize)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
pub screen_size: Vec2,
|
pub screen_size: Vec2,
|
||||||
pub mouse_pos: Vec2,
|
pub mouse_pos: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Clone, Copy, Debug, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum TextAlign {
|
pub enum TextAlign {
|
||||||
Start,
|
Start, // Test with arabic text
|
||||||
Center,
|
Center,
|
||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Clone, Debug, Serialize)] // TODOcopy
|
||||||
#[serde(rename_all = "snake_case", tag = "kind")]
|
#[serde(rename_all = "snake_case", tag = "kind")]
|
||||||
pub enum PaintCmd {
|
pub enum PaintCmd {
|
||||||
Clear {
|
Clear {
|
||||||
|
|
Loading…
Reference in a new issue