the ui is finally here
This commit is contained in:
parent
09eb8f134c
commit
6b2aa9fe14
BIN
adamina.ttf
Normal file
BIN
adamina.ttf
Normal file
Binary file not shown.
BIN
adamina.woff
Normal file
BIN
adamina.woff
Normal file
Binary file not shown.
372
src/app/bin.rs
372
src/app/bin.rs
|
@ -4,22 +4,17 @@ use std::{
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use clay_layout::{
|
mod gui;
|
||||||
Declaration,
|
|
||||||
bindings::Clay_ResetMeasureTextCache,
|
use clay_layout::render_commands::RenderCommandConfig;
|
||||||
fit, grow,
|
use gui::GUI;
|
||||||
id::Id,
|
|
||||||
layout::{LayoutDirection, Padding},
|
|
||||||
math::Dimensions,
|
|
||||||
render_commands::RenderCommandConfig,
|
|
||||||
text::{TextConfig, TextElementConfig},
|
|
||||||
};
|
|
||||||
use raylib::{
|
use raylib::{
|
||||||
RaylibHandle, RaylibThread,
|
RaylibHandle, RaylibThread,
|
||||||
color::Color,
|
color::Color,
|
||||||
ffi::Vector2,
|
ffi::{MouseButton, TextLength, Vector2},
|
||||||
prelude::{Font, RaylibDraw, RaylibDrawHandle, RaylibScissorModeExt},
|
prelude::{Font, RaylibDraw, RaylibDrawHandle},
|
||||||
text::RaylibFont,
|
text::RaylibFont,
|
||||||
|
texture::Texture2D,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FontStore {
|
struct FontStore {
|
||||||
|
@ -29,9 +24,9 @@ struct FontStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontStore {
|
impl FontStore {
|
||||||
const SIZE_PARAGRAPH: f32 = 20.;
|
// const SIZE_PARAGRAPH: f32 = 20.;
|
||||||
const SIZE_HEADER: f32 = 30.;
|
// const SIZE_HEADER: f32 = 30.;
|
||||||
const MOD_SUBTEXT: f32 = 0.5;
|
// const MOD_SUBTEXT: f32 = 0.5;
|
||||||
|
|
||||||
pub fn new(name: impl Into<String>) -> Self {
|
pub fn new(name: impl Into<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -51,27 +46,30 @@ impl FontStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_no_load(&self, size: i32) -> Option<Rc<Font>> {
|
// pub fn get_no_load(&self, size: i32) -> Option<Rc<Font>> {
|
||||||
if let Some(font) = self.fonts.get(&size) {
|
// if let Some(font) = self.fonts.get(&size) {
|
||||||
Some(font.clone())
|
// Some(font.clone())
|
||||||
} else {
|
// } else {
|
||||||
None
|
// None
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn load_fonts(
|
pub fn load_fonts(
|
||||||
&mut self,
|
&mut self,
|
||||||
handle: &mut RaylibHandle,
|
handle: &mut RaylibHandle,
|
||||||
thread: &RaylibThread,
|
thread: &RaylibThread,
|
||||||
) -> Result<(), String> {
|
) -> Result<bool, String> {
|
||||||
for size in self.fonts_to_be_loaded.clone() {
|
let invalidate = self.fonts_to_be_loaded.len() > 0;
|
||||||
assert!(size != 0, "HOW IS THIS ZERO?");
|
if invalidate {
|
||||||
println!("Loading {} in {size}px", self.font);
|
for size in self.fonts_to_be_loaded.clone() {
|
||||||
let font = Rc::new(handle.load_font_ex(&thread, &self.font, size, None)?);
|
assert!(size != 0, "HOW IS THIS ZERO?");
|
||||||
self.fonts.insert(size, font.clone());
|
println!("Loading {} in {size}px", self.font);
|
||||||
|
let font = Rc::new(handle.load_font_ex(&thread, &self.font, size, None)?);
|
||||||
|
self.fonts.insert(size, font.clone());
|
||||||
|
}
|
||||||
|
self.fonts_to_be_loaded.clear();
|
||||||
}
|
}
|
||||||
self.fonts_to_be_loaded.clear();
|
Ok(invalidate)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text(
|
pub fn text(
|
||||||
|
@ -102,10 +100,10 @@ impl FontStore {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clay2ray(c: clay_layout::color::Color) -> Color {
|
fn clay2ray(c: clay_layout::color::Color) -> Color {
|
||||||
Color {
|
Color {
|
||||||
r: (c.r as f32 * 256.0) as u8,
|
r: c.r as u8,
|
||||||
g: (c.g as f32 * 256.0) as u8,
|
g: c.g as u8,
|
||||||
b: (c.b as f32 * 256.0) as u8,
|
b: c.b as u8,
|
||||||
a: (c.a as f32 * 256.0) as u8,
|
a: c.a as u8,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,24 +117,24 @@ fn main() -> Result<(), String> {
|
||||||
.resizable()
|
.resizable()
|
||||||
.build();
|
.build();
|
||||||
let mut clay = clay_layout::Clay::new((640., 480.).into());
|
let mut clay = clay_layout::Clay::new((640., 480.).into());
|
||||||
// let font = match rl.load_font_ex(&thread, "MononokiNerdFont-Regular.ttf", 20, None) {
|
let font_store = Rc::new(RefCell::new(FontStore::new("adamina.ttf")));
|
||||||
// Ok(font) => font,
|
|
||||||
// Err(err) => panic!("Failed to load font {}", err),
|
|
||||||
// };
|
|
||||||
let font_store = Rc::new(RefCell::new(FontStore::new("MononokiNerdFont-Regular.ttf")));
|
|
||||||
let font_store_math = Rc::new(RefCell::new(FontStore::new("xits-italic.ttf")));
|
let font_store_math = Rc::new(RefCell::new(FontStore::new("xits-italic.ttf")));
|
||||||
|
|
||||||
{
|
let mut gui = GUI::new();
|
||||||
font_store.borrow_mut().get(16);
|
|
||||||
}
|
let mut previous_left_button = false;
|
||||||
|
let mut previous_right_button = false;
|
||||||
|
|
||||||
// stupid fucking 'static closures
|
// stupid fucking 'static closures
|
||||||
let font_store_clone = font_store.clone();
|
let font_store_clone = font_store.clone();
|
||||||
let font_store_math_clone = font_store.clone();
|
let font_store_math_clone = font_store_math.clone();
|
||||||
|
|
||||||
clay.set_measure_text_function(move |text, config| {
|
clay.set_measure_text_function(move |text, config| {
|
||||||
let mut text_size = clay_layout::math::Dimensions::new(0., 0.);
|
let mut text_size = clay_layout::math::Dimensions::new(0., 0.);
|
||||||
|
|
||||||
|
let mut max_text_width: f32 = 0.;
|
||||||
|
let mut line_text_width: f32 = 0.;
|
||||||
|
|
||||||
let font_store = match config.font_id {
|
let font_store = match config.font_id {
|
||||||
0 => font_store_clone.clone(),
|
0 => font_store_clone.clone(),
|
||||||
1 => font_store_math_clone.clone(),
|
1 => font_store_math_clone.clone(),
|
||||||
|
@ -146,154 +144,110 @@ fn main() -> Result<(), String> {
|
||||||
let font = if let Some(font) = font_store.borrow_mut().get(config.font_size.into()) {
|
let font = if let Some(font) = font_store.borrow_mut().get(config.font_size.into()) {
|
||||||
font
|
font
|
||||||
} else {
|
} else {
|
||||||
println!("Font not cached. Returning: {:?}", text_size);
|
// println!("Font not cached. Returning: {:?}", text_size);
|
||||||
return text_size;
|
return text_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = font.measure_text(text, config.font_size.into(), config.letter_spacing.into());
|
let scale_factor = config.font_size as f32 / font.baseSize as f32;
|
||||||
|
|
||||||
text_size.width = size.x;
|
let text_height = config.font_size as f32;
|
||||||
text_size.height = size.y;
|
|
||||||
|
|
||||||
println!("Measurements: {:?}", text_size);
|
for char in text.chars() {
|
||||||
|
if char == '\n' {
|
||||||
|
max_text_width = max_text_width.max(line_text_width);
|
||||||
|
line_text_width = 0.;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = char as u32 - 32;
|
||||||
|
line_text_width += if let Some(glyph) = font.chars().get(idx as usize) {
|
||||||
|
if glyph.advanceX > 0 {
|
||||||
|
glyph.advanceX as f32
|
||||||
|
} else {
|
||||||
|
font.get_glyph_atlas_rec(char).width + glyph.offsetX as f32
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0 as f32
|
||||||
|
} + config.letter_spacing as f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_text_width = max_text_width.max(line_text_width);
|
||||||
|
|
||||||
|
text_size.width = max_text_width * scale_factor;
|
||||||
|
text_size.height = text_height;
|
||||||
|
|
||||||
text_size
|
text_size
|
||||||
|
|
||||||
|
// unsafe {
|
||||||
|
// std::mem::transmute(font.measure_text(text, config.font_size as f32, config.letter_spacing as f32 + 1.))
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
rl.set_target_fps(30);
|
rl.set_target_fps(60);
|
||||||
|
rl.set_exit_key(None);
|
||||||
|
|
||||||
let mut debug = false;
|
let mut debug = false;
|
||||||
|
|
||||||
while !rl.window_should_close() {
|
while !rl.window_should_close() {
|
||||||
if rl.is_window_resized() {
|
if rl.is_window_resized() {
|
||||||
let size = (rl.get_screen_width() as f32 - 200., rl.get_screen_height() as f32);
|
let size = (rl.get_screen_width() as f32, rl.get_screen_height() as f32);
|
||||||
clay.layout_dimensions(size.into());
|
clay.layout_dimensions(size.into());
|
||||||
println!("Resized window to {:?}", size);
|
println!("Resized window to {:?}", size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let left_button = rl.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT);
|
||||||
|
let right_button = rl.is_mouse_button_down(MouseButton::MOUSE_BUTTON_LEFT);
|
||||||
|
|
||||||
{
|
{
|
||||||
font_store.borrow_mut().load_fonts(&mut rl, &thread)?;
|
if font_store.borrow_mut().load_fonts(&mut rl, &thread)?
|
||||||
font_store_math.borrow_mut().load_fonts(&mut rl, &thread)?;
|
|| font_store_math.borrow_mut().load_fonts(&mut rl, &thread)?
|
||||||
|
{
|
||||||
|
clay.reset_measure_text_cache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rl.is_key_pressed(raylib::ffi::KeyboardKey::KEY_D) {
|
if rl.is_key_down(raylib::ffi::KeyboardKey::KEY_RIGHT_ALT)
|
||||||
debug = !debug;
|
&& rl.is_key_pressed(raylib::ffi::KeyboardKey::KEY_D)
|
||||||
|
{
|
||||||
|
debug = !clay.debug_mode_enabled();
|
||||||
clay.reset_measure_text_cache();
|
clay.reset_measure_text_cache();
|
||||||
clay.enable_debug_mode(debug);
|
clay.enable_debug_mode(debug);
|
||||||
println!("Toggled debug mode to {debug}");
|
println!("Toggled debug mode to {debug}");
|
||||||
}
|
}
|
||||||
|
|
||||||
clay.set_debug_highlight((0xff, 0x0, 0x0).into());
|
clay.set_debug_highlight((180, 142, 173, 150).into());
|
||||||
|
|
||||||
clay.begin();
|
clay.update_scroll_containers(
|
||||||
|
false,
|
||||||
clay.with(
|
unsafe { std::mem::transmute(rl.get_mouse_wheel_move_v()) },
|
||||||
Declaration::new()
|
rl.get_frame_time(),
|
||||||
.id(clay.id("body"))
|
|
||||||
.layout()
|
|
||||||
.width(grow!(0.))
|
|
||||||
.height(grow!(0.))
|
|
||||||
.direction(LayoutDirection::TopToBottom)
|
|
||||||
.padding(Padding::all(8))
|
|
||||||
.child_gap(0)
|
|
||||||
.end()
|
|
||||||
.scroll(false, true),
|
|
||||||
|clay| {
|
|
||||||
for i in 0..0 {
|
|
||||||
clay.with(
|
|
||||||
Declaration::new()
|
|
||||||
.id(clay.id(i.to_string().as_str()))
|
|
||||||
.layout()
|
|
||||||
.width(grow!(0.))
|
|
||||||
.height(fit!(0.))
|
|
||||||
.padding(Padding::all(4))
|
|
||||||
.end(),
|
|
||||||
|clay| {
|
|
||||||
clay.text(
|
|
||||||
"test",
|
|
||||||
TextConfig::new()
|
|
||||||
.color((0x0, 0x0, 0x0).into())
|
|
||||||
.font_size(16)
|
|
||||||
.end(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let render_commands = clay.end();
|
clay.pointer_state(
|
||||||
|
unsafe { std::mem::transmute(rl.get_mouse_position()) },
|
||||||
|
left_button,
|
||||||
|
);
|
||||||
|
|
||||||
|
let render_commands = gui.render(
|
||||||
|
&clay,
|
||||||
|
(left_button, left_button && !previous_left_button),
|
||||||
|
(right_button, right_button && !previous_right_button),
|
||||||
|
(rl.get_char_pressed(), rl.get_key_pressed_number()),
|
||||||
|
rl.get_time()
|
||||||
|
);
|
||||||
|
|
||||||
let mut d = rl.begin_drawing(&thread);
|
let mut d = rl.begin_drawing(&thread);
|
||||||
|
|
||||||
d.clear_background(Color::WHITE);
|
d.clear_background(Color::WHITE);
|
||||||
|
|
||||||
/*
|
|
||||||
for (int j = 0; j < renderCommands.length; j++)
|
|
||||||
{
|
|
||||||
Clay_RenderCommand *renderCommand = Clay_RenderCommandArray_Get(&renderCommands, j);
|
|
||||||
Clay_BoundingBox boundingBox = renderCommand->boundingBox;
|
|
||||||
switch (renderCommand->commandType)
|
|
||||||
{
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_TEXT: {
|
|
||||||
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
|
|
||||||
Clay_TextRenderData *textData = &renderCommand->renderData.text;
|
|
||||||
char *cloned = (char *)malloc(textData->stringContents.length + 1);
|
|
||||||
memcpy(cloned, textData->stringContents.chars, textData->stringContents.length);
|
|
||||||
cloned[textData->stringContents.length] = '\0';
|
|
||||||
Font fontToUse = fonts[textData->fontId];
|
|
||||||
DrawTextEx(fontToUse, cloned, (Vector2){boundingBox.x, boundingBox.y}, (float)textData->fontSize, (float)textData->letterSpacing, CLAY_COLOR_TO_RAYLIB_COLOR(textData->textColor));
|
|
||||||
free(cloned);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_IMAGE: {
|
|
||||||
Texture2D imageTexture = *(Texture2D *)renderCommand->renderData.image.imageData;
|
|
||||||
Clay_Color tintColor = renderCommand->renderData.image.backgroundColor;
|
|
||||||
if (tintColor.r == 0 && tintColor.g == 0 && tintColor.b == 0 && tintColor.a == 0) {
|
|
||||||
tintColor = (Clay_Color) { 255, 255, 255, 255 };
|
|
||||||
}
|
|
||||||
DrawTextureEx(
|
|
||||||
imageTexture,
|
|
||||||
(Vector2){boundingBox.x, boundingBox.y},
|
|
||||||
0,
|
|
||||||
boundingBox.width / (float)imageTexture.width,
|
|
||||||
CLAY_COLOR_TO_RAYLIB_COLOR(tintColor));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_RECTANGLE: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CLAY_RENDER_COMMAND_TYPE_CUSTOM: {
|
|
||||||
Clay_CustomRenderData *config = &renderCommand->renderData.custom;
|
|
||||||
CustomLayoutElement *customElement = (CustomLayoutElement *)config->customData;
|
|
||||||
if (!customElement) continue;
|
|
||||||
switch (customElement->type) {
|
|
||||||
case CUSTOM_LAYOUT_ELEMENT_TYPE_3D_MODEL: {
|
|
||||||
Clay_BoundingBox rootBox = renderCommands.internalArray[0].boundingBox;
|
|
||||||
float scaleValue = CLAY__MIN(CLAY__MIN(1, 768 / rootBox.height) * CLAY__MAX(1, rootBox.width / 1024), 1.5f);
|
|
||||||
Ray positionRay = GetScreenToWorldPointWithZDistance((Vector2) { renderCommand->boundingBox.x + renderCommand->boundingBox.width / 2, renderCommand->boundingBox.y + (renderCommand->boundingBox.height / 2) + 20 }, Raylib_camera, (int)roundf(rootBox.width), (int)roundf(rootBox.height), 140);
|
|
||||||
BeginMode3D(Raylib_camera);
|
|
||||||
DrawModel(customElement->customData.model.model, positionRay.position, customElement->customData.model.scale * scaleValue, WHITE); // Draw 3d model with texture
|
|
||||||
EndMode3D();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
printf("Error: unhandled render command.");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
for command in render_commands {
|
for command in render_commands {
|
||||||
match command.config {
|
match command.config {
|
||||||
RenderCommandConfig::Rectangle(rectangle) => {
|
RenderCommandConfig::Rectangle(rectangle) => {
|
||||||
println!("RECT!!! {}: {:?}\n{:?}\n\n", command.id, command.bounding_box, rectangle);
|
// println!(
|
||||||
|
// "RECT!!! {}: {:?}\n{:?}\n\n",
|
||||||
|
// command.id, command.bounding_box, rectangle
|
||||||
|
// );
|
||||||
|
//
|
||||||
d.draw_rectangle_rounded(
|
d.draw_rectangle_rounded(
|
||||||
raylib::ffi::Rectangle {
|
raylib::ffi::Rectangle {
|
||||||
x: command.bounding_box.x,
|
x: command.bounding_box.x,
|
||||||
|
@ -307,8 +261,13 @@ fn main() -> Result<(), String> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RenderCommandConfig::Text(text) => {
|
RenderCommandConfig::Text(text) => {
|
||||||
// println!("{:?}", text);
|
let font = match text.font_id {
|
||||||
font_store.borrow_mut().text(
|
0 => font_store.clone(),
|
||||||
|
1 => font_store_math.clone(),
|
||||||
|
_ => panic!("Unknown font {}", text.font_id),
|
||||||
|
};
|
||||||
|
|
||||||
|
font.borrow_mut().text(
|
||||||
&mut d,
|
&mut d,
|
||||||
text.text,
|
text.text,
|
||||||
command.bounding_box.x,
|
command.bounding_box.x,
|
||||||
|
@ -318,7 +277,6 @@ fn main() -> Result<(), String> {
|
||||||
Some(clay2ray(text.color)),
|
Some(clay2ray(text.color)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderCommandConfig::ScissorStart() => unsafe {
|
RenderCommandConfig::ScissorStart() => unsafe {
|
||||||
raylib::ffi::BeginScissorMode(
|
raylib::ffi::BeginScissorMode(
|
||||||
command.bounding_box.x as i32,
|
command.bounding_box.x as i32,
|
||||||
|
@ -330,22 +288,7 @@ fn main() -> Result<(), String> {
|
||||||
RenderCommandConfig::ScissorEnd() => unsafe {
|
RenderCommandConfig::ScissorEnd() => unsafe {
|
||||||
raylib::ffi::EndScissorMode();
|
raylib::ffi::EndScissorMode();
|
||||||
},
|
},
|
||||||
|
|
||||||
RenderCommandConfig::Border(border) => {
|
RenderCommandConfig::Border(border) => {
|
||||||
// if (config->cornerRadius.topLeft > 0) {
|
|
||||||
// DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.topLeft), roundf(boundingBox.y + config->cornerRadius.topLeft) }, roundf(config->cornerRadius.topLeft - config->width.top), config->cornerRadius.topLeft, 180, 270, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
|
||||||
// }
|
|
||||||
// if (config->cornerRadius.topRight > 0) {
|
|
||||||
// DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.topRight), roundf(boundingBox.y + config->cornerRadius.topRight) }, roundf(config->cornerRadius.topRight - config->width.top), config->cornerRadius.topRight, 270, 360, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
|
||||||
// }
|
|
||||||
// if (config->cornerRadius.bottomLeft > 0) {
|
|
||||||
// DrawRing((Vector2) { roundf(boundingBox.x + config->cornerRadius.bottomLeft), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomLeft) }, roundf(config->cornerRadius.bottomLeft - config->width.top), config->cornerRadius.bottomLeft, 90, 180, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
|
||||||
// }
|
|
||||||
// if (config->cornerRadius.bottomRight > 0) {
|
|
||||||
// DrawRing((Vector2) { roundf(boundingBox.x + boundingBox.width - config->cornerRadius.bottomRight), roundf(boundingBox.y + boundingBox.height - config->cornerRadius.bottomRight) }, roundf(config->cornerRadius.bottomRight - config->width.bottom), config->cornerRadius.bottomRight, 0.1, 90, 10, CLAY_COLOR_TO_RAYLIB_COLOR(config->color));
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// Left border
|
// Left border
|
||||||
if border.width.left > 0 {
|
if border.width.left > 0 {
|
||||||
d.draw_rectangle(
|
d.draw_rectangle(
|
||||||
|
@ -399,12 +342,95 @@ fn main() -> Result<(), String> {
|
||||||
clay2ray(border.color),
|
clay2ray(border.color),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// Top left corner
|
||||||
|
if border.corner_radii.top_left > 0. {
|
||||||
|
d.draw_ring(
|
||||||
|
Vector2 {
|
||||||
|
x: command.bounding_box.x + border.corner_radii.top_left,
|
||||||
|
y: command.bounding_box.y + border.corner_radii.top_left,
|
||||||
|
},
|
||||||
|
border.corner_radii.top_left - border.width.top as f32,
|
||||||
|
border.corner_radii.top_left,
|
||||||
|
180.,
|
||||||
|
270.,
|
||||||
|
10,
|
||||||
|
clay2ray(border.color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Top right corner
|
||||||
|
if border.corner_radii.top_right > 0. {
|
||||||
|
d.draw_ring(
|
||||||
|
Vector2 {
|
||||||
|
x: command.bounding_box.x + command.bounding_box.width
|
||||||
|
- border.corner_radii.top_right,
|
||||||
|
y: command.bounding_box.y + border.corner_radii.top_right,
|
||||||
|
},
|
||||||
|
border.corner_radii.top_right - border.width.top as f32,
|
||||||
|
border.corner_radii.top_right,
|
||||||
|
270.,
|
||||||
|
360.,
|
||||||
|
10,
|
||||||
|
clay2ray(border.color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Bottom left
|
||||||
|
if border.corner_radii.bottom_left > 0. {
|
||||||
|
d.draw_ring(
|
||||||
|
Vector2 {
|
||||||
|
x: command.bounding_box.x + border.corner_radii.bottom_left,
|
||||||
|
y: command.bounding_box.y + command.bounding_box.height
|
||||||
|
- border.corner_radii.bottom_left,
|
||||||
|
},
|
||||||
|
border.corner_radii.bottom_left - border.width.top as f32,
|
||||||
|
border.corner_radii.bottom_left,
|
||||||
|
90.,
|
||||||
|
180.,
|
||||||
|
10,
|
||||||
|
clay2ray(border.color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Bottom right
|
||||||
|
if border.corner_radii.bottom_right > 0. {
|
||||||
|
d.draw_ring(
|
||||||
|
Vector2 {
|
||||||
|
x: command.bounding_box.x + command.bounding_box.width
|
||||||
|
- border.corner_radii.bottom_right,
|
||||||
|
y: command.bounding_box.y + command.bounding_box.height
|
||||||
|
- border.corner_radii.bottom_right,
|
||||||
|
},
|
||||||
|
border.corner_radii.bottom_right - border.width.bottom as f32,
|
||||||
|
border.corner_radii.bottom_right,
|
||||||
|
0.,
|
||||||
|
90.,
|
||||||
|
10,
|
||||||
|
clay2ray(border.color),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderCommandConfig::None() => {}
|
RenderCommandConfig::None() => {}
|
||||||
|
RenderCommandConfig::Image(image) => {
|
||||||
|
let texture: &Texture2D = unsafe {
|
||||||
|
std::mem::transmute::<*const std::ffi::c_void, *const Texture2D>(image.data)
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
d.draw_texture_ex(
|
||||||
|
texture,
|
||||||
|
Vector2 {
|
||||||
|
x: command.bounding_box.x,
|
||||||
|
y: command.bounding_box.y,
|
||||||
|
},
|
||||||
|
0.,
|
||||||
|
command.bounding_box.width / image.dimensions.width,
|
||||||
|
clay2ray(image.background_color),
|
||||||
|
);
|
||||||
|
}
|
||||||
_ => panic!("Unimplemented {:#?}", command),
|
_ => panic!("Unimplemented {:#?}", command),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
previous_left_button = left_button;
|
||||||
|
previous_right_button = right_button;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
224
src/app/gui/document.rs
Normal file
224
src/app/gui/document.rs
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
use std::mem::ManuallyDrop;
|
||||||
|
|
||||||
|
use clay_layout::{
|
||||||
|
Clay, Declaration, fit, fixed, grow,
|
||||||
|
layout::{LayoutDirection, Padding},
|
||||||
|
text::TextConfig,
|
||||||
|
};
|
||||||
|
use raylib::ffi::KeyboardKey;
|
||||||
|
|
||||||
|
pub type LineType = String;
|
||||||
|
|
||||||
|
pub struct Document {
|
||||||
|
lines: Vec<(String, LineType)>,
|
||||||
|
total_lines: usize,
|
||||||
|
current_line: Option<usize>,
|
||||||
|
current_char: usize,
|
||||||
|
lines_to_delete: Vec<usize>,
|
||||||
|
lines_to_add: Vec<(usize, LineType)>,
|
||||||
|
last_type_time: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Document {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
lines: vec![("0".to_owned(), "".to_string())],
|
||||||
|
total_lines: 1,
|
||||||
|
current_line: Some(0),
|
||||||
|
current_char: 0,
|
||||||
|
lines_to_delete: vec![],
|
||||||
|
lines_to_add: vec![],
|
||||||
|
last_type_time: -100.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_line(&mut self, content: impl Into<LineType>) {
|
||||||
|
self.add_line_at(self.lines.len(), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_line_at(&mut self, idx: usize, content: impl Into<LineType>) {
|
||||||
|
self.lines
|
||||||
|
.insert(idx, (self.total_lines.to_string(), content.into()));
|
||||||
|
self.total_lines += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
clay: &'a Clay,
|
||||||
|
(left_held, left_pressed): (bool, bool),
|
||||||
|
(righ_held, right_pressed): (bool, bool),
|
||||||
|
(latest_char, latest_key): (Option<char>, Option<u32>),
|
||||||
|
time: f64,
|
||||||
|
) {
|
||||||
|
if !self.lines_to_delete.is_empty() {
|
||||||
|
self.lines_to_delete.sort();
|
||||||
|
|
||||||
|
for line in self.lines_to_delete.iter().rev() {
|
||||||
|
self.lines.remove(*line);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.lines_to_delete.clear();
|
||||||
|
|
||||||
|
self.current_line = if let Some(i) = self.current_line {
|
||||||
|
if self.lines.len() == 0 {
|
||||||
|
self.add_line("".to_string());
|
||||||
|
}
|
||||||
|
Some(
|
||||||
|
(TryInto::<i32>::try_into(i).unwrap() - 1)
|
||||||
|
.max(0)
|
||||||
|
.min(TryInto::<i32>::try_into(self.lines.len()).unwrap() - 1)
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.lines_to_add.is_empty() {
|
||||||
|
// i am NOT cloning this shit
|
||||||
|
let lines_to_add: ManuallyDrop<Vec<(usize, String)>> = unsafe {
|
||||||
|
let len = self.lines_to_add.len();
|
||||||
|
let cap = self.lines_to_add.capacity();
|
||||||
|
let fuck = self.lines_to_add.as_mut_ptr();
|
||||||
|
std::mem::ManuallyDrop::new(Vec::from_raw_parts(fuck, len, cap))
|
||||||
|
};
|
||||||
|
|
||||||
|
for (idx, line) in lines_to_add.iter() {
|
||||||
|
self.add_line_at(*idx, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.lines_to_add.clear();
|
||||||
|
self.current_line = if let Some(i) = self.current_line {
|
||||||
|
Some(i + 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if left_pressed && clay.pointer_over(clay.id("document")) {
|
||||||
|
self.current_line = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = self.lines.len();
|
||||||
|
let mut can_go_down = true;
|
||||||
|
|
||||||
|
clay.with(
|
||||||
|
&Declaration::new()
|
||||||
|
.id(clay.id("document"))
|
||||||
|
.layout()
|
||||||
|
.width(grow!(0.))
|
||||||
|
.height(grow!(0.))
|
||||||
|
.direction(LayoutDirection::TopToBottom)
|
||||||
|
.padding(Padding::all(8))
|
||||||
|
.child_gap(0)
|
||||||
|
.end()
|
||||||
|
.background_color((46, 52, 64).into())
|
||||||
|
.scroll(false, true),
|
||||||
|
|clay| {
|
||||||
|
for (i, line) in self.lines.iter_mut().enumerate() {
|
||||||
|
let current_selected =
|
||||||
|
if left_pressed && clay.pointer_over(clay.id(line.0.as_str())) {
|
||||||
|
self.current_line = Some(i);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
Some(i) == self.current_line
|
||||||
|
};
|
||||||
|
|
||||||
|
if current_selected {
|
||||||
|
if let Some(char) = latest_char {
|
||||||
|
line.1 += char.to_string().as_str();
|
||||||
|
self.last_type_time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
match latest_key {
|
||||||
|
Some(val) if val == KeyboardKey::KEY_BACKSPACE as u32 => {
|
||||||
|
if line.1.len() == 0 {
|
||||||
|
self.lines_to_delete.push(i);
|
||||||
|
} else {
|
||||||
|
let _ = line.1.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(val) if val == KeyboardKey::KEY_ENTER as u32 => {
|
||||||
|
self.lines_to_add.push((i + 1, "".to_string()));
|
||||||
|
}
|
||||||
|
Some(val) if val == KeyboardKey::KEY_UP as u32 => {
|
||||||
|
self.current_line = Some(
|
||||||
|
(TryInto::<i32>::try_into(self.current_line.unwrap()).unwrap()
|
||||||
|
- 1)
|
||||||
|
.max(0)
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(val) if val == KeyboardKey::KEY_DOWN as u32 && can_go_down => {
|
||||||
|
self.current_line = Some(
|
||||||
|
(TryInto::<i32>::try_into(self.current_line.unwrap()).unwrap()
|
||||||
|
+ 1)
|
||||||
|
.min(TryInto::<i32>::try_into(len).unwrap() - 1)
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
can_go_down = false;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clay.with(
|
||||||
|
&Declaration::new()
|
||||||
|
.id(clay.id(line.0.as_str()))
|
||||||
|
.layout()
|
||||||
|
.width(grow!(0.))
|
||||||
|
.height(fit!(21.))
|
||||||
|
.direction(LayoutDirection::LeftToRight)
|
||||||
|
.padding(Padding::new(4, 4, 0, 1))
|
||||||
|
.child_alignment(clay_layout::layout::Alignment {
|
||||||
|
x: clay_layout::layout::LayoutAlignmentX::Left,
|
||||||
|
y: clay_layout::layout::LayoutAlignmentY::Center,
|
||||||
|
})
|
||||||
|
// .child_gap(2)
|
||||||
|
// .end()
|
||||||
|
// .border()
|
||||||
|
// .color((67, 76, 94).into())
|
||||||
|
// .bottom(2)
|
||||||
|
// .end()
|
||||||
|
// .corner_radius()
|
||||||
|
// .all(4.)
|
||||||
|
.end(),
|
||||||
|
|clay| {
|
||||||
|
clay.text(
|
||||||
|
line.1.as_str(),
|
||||||
|
TextConfig::new()
|
||||||
|
.color((229, 233, 240).into())
|
||||||
|
.font_size(20)
|
||||||
|
.font_id(0)
|
||||||
|
.letter_spacing(2)
|
||||||
|
.end(),
|
||||||
|
);
|
||||||
|
if current_selected
|
||||||
|
&& can_go_down
|
||||||
|
&& ((time - self.last_type_time) < 0.5 || time % 1.2 < 0.6)
|
||||||
|
{
|
||||||
|
clay.with(
|
||||||
|
&Declaration::new()
|
||||||
|
.id(clay.id("text_cursor"))
|
||||||
|
.layout()
|
||||||
|
.height(grow!(0.))
|
||||||
|
.width(fixed!(8.))
|
||||||
|
.end()
|
||||||
|
.background_color((229, 233, 240, 100).into())
|
||||||
|
// .corner_radius()
|
||||||
|
// .all(2.)
|
||||||
|
// .end(),
|
||||||
|
,
|
||||||
|
|_| {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,36 @@
|
||||||
use std::rc::Rc;
|
use clay_layout::{
|
||||||
|
Clay, Declaration, grow,
|
||||||
use libopenbirch::node::{
|
layout::{LayoutDirection, Padding},
|
||||||
NodeEnum, add::Add, constant::Constant, divide::Divide, multiply::Multiply, symbol::Symbol,
|
render_commands::RenderCommand,
|
||||||
};
|
};
|
||||||
use math_segments::MathSegment;
|
use document::Document;
|
||||||
|
|
||||||
mod math_segments;
|
mod document;
|
||||||
|
|
||||||
pub struct GUI {
|
pub struct GUI {
|
||||||
meth: Vec<Rc<dyn MathSegment>>,
|
pub document: Document,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GUI {
|
impl GUI {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let a = Constant::new(69.0).into();
|
|
||||||
let b = Constant::new(420.0).into();
|
|
||||||
let c: NodeEnum = Add::new(a, b).into();
|
|
||||||
let d = Symbol::new_from_str("x").into();
|
|
||||||
let e = Divide::new(c.clone(), d).into();
|
|
||||||
let f = Multiply::new(e, c);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
meth: vec![Rc::new(NodeEnum::Multiply(f))],
|
document: Document::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn render<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
clay: &'a Clay,
|
||||||
|
left_mouse: (bool, bool),
|
||||||
|
right_mouse: (bool, bool),
|
||||||
|
latest_char: (Option<char>, Option<u32>),
|
||||||
|
time: f64,
|
||||||
|
) -> impl Iterator<Item = RenderCommand> {
|
||||||
|
clay.begin();
|
||||||
|
|
||||||
|
self.document
|
||||||
|
.render(clay, left_mouse, right_mouse, latest_char, time);
|
||||||
|
|
||||||
|
clay.end()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{Environment, Node, NodeEnum, Precedence, empty::Empty, symbol::Symbol};
|
use super::{Environment, Node, NodeEnum, Precedence, empty::Empty};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||||
pub struct Assign {
|
pub struct Assign {
|
||||||
|
|
BIN
xits-italic.otf
BIN
xits-italic.otf
Binary file not shown.
Loading…
Reference in a new issue