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,
|
||||
};
|
||||
|
||||
use clay_layout::{
|
||||
Declaration,
|
||||
bindings::Clay_ResetMeasureTextCache,
|
||||
fit, grow,
|
||||
id::Id,
|
||||
layout::{LayoutDirection, Padding},
|
||||
math::Dimensions,
|
||||
render_commands::RenderCommandConfig,
|
||||
text::{TextConfig, TextElementConfig},
|
||||
};
|
||||
mod gui;
|
||||
|
||||
use clay_layout::render_commands::RenderCommandConfig;
|
||||
use gui::GUI;
|
||||
use raylib::{
|
||||
RaylibHandle, RaylibThread,
|
||||
color::Color,
|
||||
ffi::Vector2,
|
||||
prelude::{Font, RaylibDraw, RaylibDrawHandle, RaylibScissorModeExt},
|
||||
ffi::{MouseButton, TextLength, Vector2},
|
||||
prelude::{Font, RaylibDraw, RaylibDrawHandle},
|
||||
text::RaylibFont,
|
||||
texture::Texture2D,
|
||||
};
|
||||
|
||||
struct FontStore {
|
||||
|
@ -29,9 +24,9 @@ struct FontStore {
|
|||
}
|
||||
|
||||
impl FontStore {
|
||||
const SIZE_PARAGRAPH: f32 = 20.;
|
||||
const SIZE_HEADER: f32 = 30.;
|
||||
const MOD_SUBTEXT: f32 = 0.5;
|
||||
// const SIZE_PARAGRAPH: f32 = 20.;
|
||||
// const SIZE_HEADER: f32 = 30.;
|
||||
// const MOD_SUBTEXT: f32 = 0.5;
|
||||
|
||||
pub fn new(name: impl Into<String>) -> Self {
|
||||
Self {
|
||||
|
@ -51,27 +46,30 @@ impl FontStore {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_no_load(&self, size: i32) -> Option<Rc<Font>> {
|
||||
if let Some(font) = self.fonts.get(&size) {
|
||||
Some(font.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// pub fn get_no_load(&self, size: i32) -> Option<Rc<Font>> {
|
||||
// if let Some(font) = self.fonts.get(&size) {
|
||||
// Some(font.clone())
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn load_fonts(
|
||||
&mut self,
|
||||
handle: &mut RaylibHandle,
|
||||
thread: &RaylibThread,
|
||||
) -> Result<(), String> {
|
||||
for size in self.fonts_to_be_loaded.clone() {
|
||||
assert!(size != 0, "HOW IS THIS ZERO?");
|
||||
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());
|
||||
) -> Result<bool, String> {
|
||||
let invalidate = self.fonts_to_be_loaded.len() > 0;
|
||||
if invalidate {
|
||||
for size in self.fonts_to_be_loaded.clone() {
|
||||
assert!(size != 0, "HOW IS THIS ZERO?");
|
||||
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(())
|
||||
Ok(invalidate)
|
||||
}
|
||||
|
||||
pub fn text(
|
||||
|
@ -102,10 +100,10 @@ impl FontStore {
|
|||
#[inline]
|
||||
fn clay2ray(c: clay_layout::color::Color) -> Color {
|
||||
Color {
|
||||
r: (c.r as f32 * 256.0) as u8,
|
||||
g: (c.g as f32 * 256.0) as u8,
|
||||
b: (c.b as f32 * 256.0) as u8,
|
||||
a: (c.a as f32 * 256.0) as u8,
|
||||
r: c.r as u8,
|
||||
g: c.g as u8,
|
||||
b: c.b as u8,
|
||||
a: c.a as u8,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,24 +117,24 @@ fn main() -> Result<(), String> {
|
|||
.resizable()
|
||||
.build();
|
||||
let mut clay = clay_layout::Clay::new((640., 480.).into());
|
||||
// let font = match rl.load_font_ex(&thread, "MononokiNerdFont-Regular.ttf", 20, None) {
|
||||
// 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 = Rc::new(RefCell::new(FontStore::new("adamina.ttf")));
|
||||
let font_store_math = Rc::new(RefCell::new(FontStore::new("xits-italic.ttf")));
|
||||
|
||||
{
|
||||
font_store.borrow_mut().get(16);
|
||||
}
|
||||
let mut gui = GUI::new();
|
||||
|
||||
let mut previous_left_button = false;
|
||||
let mut previous_right_button = false;
|
||||
|
||||
// stupid fucking 'static closures
|
||||
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| {
|
||||
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 {
|
||||
0 => font_store_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()) {
|
||||
font
|
||||
} else {
|
||||
println!("Font not cached. Returning: {:?}", text_size);
|
||||
// println!("Font not cached. Returning: {:?}", 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;
|
||||
text_size.height = size.y;
|
||||
let text_height = config.font_size as f32;
|
||||
|
||||
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
|
||||
|
||||
// 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;
|
||||
|
||||
while !rl.window_should_close() {
|
||||
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());
|
||||
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)?;
|
||||
font_store_math.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)?
|
||||
{
|
||||
clay.reset_measure_text_cache();
|
||||
}
|
||||
}
|
||||
|
||||
if rl.is_key_pressed(raylib::ffi::KeyboardKey::KEY_D) {
|
||||
debug = !debug;
|
||||
if rl.is_key_down(raylib::ffi::KeyboardKey::KEY_RIGHT_ALT)
|
||||
&& rl.is_key_pressed(raylib::ffi::KeyboardKey::KEY_D)
|
||||
{
|
||||
debug = !clay.debug_mode_enabled();
|
||||
clay.reset_measure_text_cache();
|
||||
clay.enable_debug_mode(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.with(
|
||||
Declaration::new()
|
||||
.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(),
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
clay.update_scroll_containers(
|
||||
false,
|
||||
unsafe { std::mem::transmute(rl.get_mouse_wheel_move_v()) },
|
||||
rl.get_frame_time(),
|
||||
);
|
||||
|
||||
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);
|
||||
|
||||
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 {
|
||||
match command.config {
|
||||
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(
|
||||
raylib::ffi::Rectangle {
|
||||
x: command.bounding_box.x,
|
||||
|
@ -307,8 +261,13 @@ fn main() -> Result<(), String> {
|
|||
);
|
||||
}
|
||||
RenderCommandConfig::Text(text) => {
|
||||
// println!("{:?}", text);
|
||||
font_store.borrow_mut().text(
|
||||
let font = match text.font_id {
|
||||
0 => font_store.clone(),
|
||||
1 => font_store_math.clone(),
|
||||
_ => panic!("Unknown font {}", text.font_id),
|
||||
};
|
||||
|
||||
font.borrow_mut().text(
|
||||
&mut d,
|
||||
text.text,
|
||||
command.bounding_box.x,
|
||||
|
@ -318,7 +277,6 @@ fn main() -> Result<(), String> {
|
|||
Some(clay2ray(text.color)),
|
||||
);
|
||||
}
|
||||
|
||||
RenderCommandConfig::ScissorStart() => unsafe {
|
||||
raylib::ffi::BeginScissorMode(
|
||||
command.bounding_box.x as i32,
|
||||
|
@ -330,22 +288,7 @@ fn main() -> Result<(), String> {
|
|||
RenderCommandConfig::ScissorEnd() => unsafe {
|
||||
raylib::ffi::EndScissorMode();
|
||||
},
|
||||
|
||||
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
|
||||
if border.width.left > 0 {
|
||||
d.draw_rectangle(
|
||||
|
@ -399,12 +342,95 @@ fn main() -> Result<(), String> {
|
|||
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::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),
|
||||
}
|
||||
}
|
||||
|
||||
previous_left_button = left_button;
|
||||
previous_right_button = right_button;
|
||||
}
|
||||
|
||||
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 libopenbirch::node::{
|
||||
NodeEnum, add::Add, constant::Constant, divide::Divide, multiply::Multiply, symbol::Symbol,
|
||||
use clay_layout::{
|
||||
Clay, Declaration, grow,
|
||||
layout::{LayoutDirection, Padding},
|
||||
render_commands::RenderCommand,
|
||||
};
|
||||
use math_segments::MathSegment;
|
||||
use document::Document;
|
||||
|
||||
mod math_segments;
|
||||
mod document;
|
||||
|
||||
pub struct GUI {
|
||||
meth: Vec<Rc<dyn MathSegment>>,
|
||||
pub document: Document,
|
||||
}
|
||||
|
||||
impl GUI {
|
||||
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 {
|
||||
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 super::{Environment, Node, NodeEnum, Precedence, empty::Empty, symbol::Symbol};
|
||||
use super::{Environment, Node, NodeEnum, Precedence, empty::Empty};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd)]
|
||||
pub struct Assign {
|
||||
|
|
BIN
xits-italic.otf
BIN
xits-italic.otf
Binary file not shown.
Loading…
Reference in a new issue