-
Notifications
You must be signed in to change notification settings - Fork 3
Lokui: GUI framework experiment #5
base: main
Are you sure you want to change the base?
Changes from 29 commits
468dc30
6054eef
cb469f6
3a73fb4
432a955
acd189a
502494e
6ebb16a
38d06ca
ed737bb
e9dc989
b88dd1e
25fce4f
ad39404
bd6c2b9
29e6b54
4bd9b0e
46d59e4
008451f
8726f8b
73d659e
f5a05dc
a1350ff
d1174ba
fee22f3
045e040
cc6e3b6
e8fa294
6b34435
547580c
241aad8
1b36d4f
7edfde9
a927665
289f752
6393c0d
1aed813
27f1aed
b2a4f5f
58f247d
363a9f0
9a7247a
8fcefa3
cf11755
3845d90
6b6cb6e
3fdf34b
bbd32b9
6508348
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| #![allow(clippy::unusual_byte_groupings)] | ||
|
|
||
| use std::io::{stdout, BufWriter, Write}; | ||
|
|
||
| use lokui::components::button::button; | ||
| use lokui::components::pane::{pane, Pane}; | ||
| use lokui::components::text::text; | ||
| use lokui::events::{Event, MousePosition}; | ||
| use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; | ||
| use lokui::lazy::{laz, lazy}; | ||
| use lokui::widget::Widget; | ||
| use miniquad::skia::SkiaContext; | ||
| use miniquad::{conf, EventHandler}; | ||
| use skia_safe::{Color, Font, FontStyle, Typeface}; | ||
|
|
||
| fn counter() -> Pane { | ||
| let value = laz(0); | ||
|
|
||
| let increment = { | ||
| let value = value.clone(); | ||
| move |_, _| { | ||
| value.set(value.get() + 1); | ||
| println!("+1! Counter = {}", value.get()); | ||
| } | ||
| }; | ||
|
|
||
| let decrement = { | ||
| let value = value.clone(); | ||
| move |_, _| { | ||
| value.set(value.get() - 1); | ||
| println!("-1! Counter = {}", value.get()); | ||
| } | ||
| }; | ||
|
AshesOfEther marked this conversation as resolved.
Outdated
|
||
|
|
||
| let typeface = Typeface::new("Torus Pro", FontStyle::normal()).unwrap(); | ||
| let font = lazy(Font::new(typeface, Some(20.))); | ||
|
|
||
| pane() | ||
| .with_padding(Padding::splat(10.)) | ||
| .with_layout( | ||
| Layout::new() | ||
| .with_anchor(Anchor::CENTER) | ||
| .with_dimension(DimScalar::Fixed(400.), DimScalar::Fixed(250.)), | ||
| ) | ||
|
AshesOfEther marked this conversation as resolved.
Outdated
|
||
| .child( | ||
|
AshesOfEther marked this conversation as resolved.
|
||
| pane() | ||
| .with_padding(Padding::vh(5., 10.)) | ||
| .with_flex_layout(FlexLayout { | ||
| direction: Direction::Horizontal, | ||
| gap: 5., | ||
| }) | ||
| .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) | ||
|
AshesOfEther marked this conversation as resolved.
Outdated
|
||
| .child( | ||
| pane() | ||
| .with_layout( | ||
| Layout::new() | ||
| .with_dimension(DimScalar::Fill, DimScalar::Fixed(50.)) | ||
| .with_origin(Anchor::CENTER) | ||
| .with_anchor(Anchor::CENTER), | ||
| ) | ||
| .child(text(value, font.clone())), | ||
| ) | ||
| .child( | ||
| button(text("+1", font.clone())) | ||
| .with_layout( | ||
| Layout::new() | ||
| .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) | ||
| .with_origin(Anchor::CENTER) | ||
| .with_anchor(Anchor::CENTER), | ||
| ) | ||
| .on_click(increment), | ||
| ) | ||
| .child( | ||
| button(text("-1", font)) | ||
| .with_layout( | ||
| Layout::new() | ||
| .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) | ||
| .with_origin(Anchor::CENTER) | ||
| .with_anchor(Anchor::CENTER), | ||
| ) | ||
| .on_click(decrement), | ||
| ), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A way to avoid writing almost the same thing three separate times would be nice.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lol I didn't bother not repeating. xD
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course, layout is what I meant to be referring to.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah alright |
||
| ) | ||
| } | ||
|
|
||
| struct Stage { | ||
| root_pane: Pane, | ||
| root_layout: SolvedLayout, | ||
| window_layout: SolvedLayout, | ||
| } | ||
|
|
||
| impl EventHandler for Stage { | ||
| fn update(&mut self, _skia_ctx: &mut SkiaContext) {} | ||
|
|
||
| fn mouse_button_down_event( | ||
| &mut self, | ||
| _skia_ctx: &mut SkiaContext, | ||
| _button: miniquad::MouseButton, | ||
| x: f32, | ||
| y: f32, | ||
| ) { | ||
| let event = Event::MouseDown(MousePosition { x, y }); | ||
| self.root_pane.handle_event(event, &self.root_layout); | ||
| } | ||
|
|
||
| fn mouse_button_up_event( | ||
| &mut self, | ||
| _skia_ctx: &mut SkiaContext, | ||
| _button: miniquad::MouseButton, | ||
| x: f32, | ||
| y: f32, | ||
| ) { | ||
| let event = Event::MouseUp(MousePosition { x, y }); | ||
| self.root_pane.handle_event(event, &self.root_layout); | ||
| } | ||
|
|
||
| fn mouse_motion_event(&mut self, _skia_ctx: &mut SkiaContext, x: f32, y: f32) { | ||
| let event = Event::MouseMove(MousePosition { x, y }); | ||
| self.root_pane.handle_event(event, &self.root_layout); | ||
| } | ||
|
|
||
| fn resize_event(&mut self, skia_ctx: &mut SkiaContext, width: f32, height: f32) { | ||
| self.window_layout = SolvedLayout::from_top_left(0., 0., width, height); | ||
| self.root_layout = self.root_pane.solve_layout(&self.window_layout); | ||
| skia_ctx.recreate_surface(width as i32, height as i32); | ||
| } | ||
|
|
||
| fn draw(&mut self, skia_ctx: &mut SkiaContext) { | ||
| let canvas = skia_ctx.surface.canvas(); | ||
| canvas.clear(Color::from(0xff_161a1d)); | ||
| self.root_pane.draw(canvas, &self.root_layout); | ||
| skia_ctx.dctx.flush(None); | ||
| } | ||
| } | ||
|
|
||
| fn main() { | ||
| let mut root_pane = counter(); | ||
| let window_layout = SolvedLayout::from_top_left(0., 0., 1280., 720.); | ||
| let root_layout = root_pane.solve_layout(&window_layout); | ||
|
|
||
| let mut writer = BufWriter::new(stdout()); | ||
| root_pane.debug(&mut writer, 0).unwrap(); | ||
| writer.flush().unwrap(); | ||
|
|
||
| miniquad::start( | ||
| conf::Conf { | ||
| high_dpi: true, | ||
| window_width: 1280, | ||
| window_height: 720, | ||
| window_title: "Lokui GUI Framework Prototype".to_owned(), | ||
| ..Default::default() | ||
| }, | ||
| move || { | ||
| Box::new(Stage { | ||
| root_pane, | ||
| root_layout, | ||
| window_layout, | ||
| }) | ||
| }, | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| use std::fmt::Display; | ||
| use std::io; | ||
|
|
||
| use skia_safe::{Canvas, Color, Paint, Rect}; | ||
|
|
||
| use crate::events::{Event, MousePosition}; | ||
| use crate::indentation; | ||
| use crate::layout::{Layout, Padding, SolvedLayout}; | ||
| use crate::lazy::Laz; | ||
| use crate::widget::{default_solve_layout, Widget}; | ||
|
|
||
| use super::text::Text; | ||
|
|
||
| pub struct Button<T: Display> { | ||
| layout: Layout, | ||
| text_layout: SolvedLayout, | ||
| text: Text<T>, | ||
| on_click: Option<Box<dyn FnMut(f32, f32)>>, | ||
| enabled: Laz<bool>, | ||
| is_mouse_down: bool, | ||
| hovered: bool, | ||
| } | ||
|
|
||
| pub fn button<T: Display>(text: Text<T>) -> Button<T> { | ||
| Button { | ||
| layout: Layout::hug(), | ||
| text_layout: SolvedLayout::default(), | ||
| text, | ||
| on_click: None, | ||
| enabled: Laz::new(true), | ||
| is_mouse_down: false, | ||
| hovered: false, | ||
| } | ||
| } | ||
|
|
||
| impl<T: Display> Button<T> { | ||
| pub fn with_layout(mut self, layout: Layout) -> Self { | ||
| self.layout = layout; | ||
| self | ||
| } | ||
|
|
||
| pub fn on_click(mut self, on_click: impl FnMut(f32, f32) + 'static) -> Self { | ||
| self.on_click = Some(Box::new(on_click)); | ||
| self | ||
| } | ||
| } | ||
|
|
||
| impl<T: Display> Widget for Button<T> { | ||
| fn layout(&self) -> &Layout { | ||
| &self.layout | ||
| } | ||
|
|
||
| fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { | ||
| let layout = default_solve_layout(self, parent_layout); | ||
| self.text_layout = (self.text).solve_layout(&layout.padded(Padding::vh(5., 10.))); | ||
| layout | ||
| } | ||
|
|
||
| fn min_width(&self) -> f32 { | ||
| self.text.min_width() + 10. | ||
| } | ||
|
|
||
| fn min_height(&self) -> f32 { | ||
| self.text.min_height() + 5. | ||
| } | ||
|
|
||
| fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { | ||
| writeln!( | ||
| w, | ||
| "{}<button>{}</button>", | ||
| indentation(deepness), | ||
| self.text.text(), | ||
| ) | ||
| } | ||
|
|
||
| fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { | ||
| let rect = Rect::from_xywh( | ||
| layout.x_start(), | ||
| layout.y_start(), | ||
| layout.width(), | ||
| layout.height(), | ||
| ); | ||
|
|
||
| let mut paint = Paint::default(); | ||
| paint.set_anti_alias(true); | ||
| paint.set_stroke_width(2.); | ||
|
|
||
| let color = match (self.enabled.get(), self.is_mouse_down) { | ||
| (true, true) => 0xa70038, | ||
| (true, false) if self.hovered => 0xff415a, | ||
| (true, false) if !self.hovered => 0xff0051, | ||
| _ => 0x7a4553, | ||
| }; | ||
|
|
||
| paint.set_stroke(false); | ||
| paint.set_color(Color::from(0x80_000000 | color)); | ||
| canvas.draw_rect(rect, &paint); | ||
|
|
||
| paint.set_stroke(true); | ||
| paint.set_color(Color::from(0xff_000000 | color)); | ||
| canvas.draw_rect(rect, &paint); | ||
|
|
||
| self.text.draw(canvas, &self.text_layout); | ||
| } | ||
|
|
||
| fn handle_event(&mut self, event: Event, _layout: &SolvedLayout) -> bool { | ||
| if self.enabled.get() { | ||
| match event { | ||
| Event::MouseDown(_) => { | ||
| self.is_mouse_down = true; | ||
| true | ||
| } | ||
| Event::MouseUp(MousePosition { x, y }) => { | ||
| if self.is_mouse_down { | ||
| self.is_mouse_down = false; | ||
| if let Some(on_click) = self.on_click.as_mut() { | ||
| (on_click)(x, y); | ||
| } | ||
| true | ||
| } else { | ||
| false | ||
| } | ||
| } | ||
| Event::MouseIn => { | ||
| self.hovered = true; | ||
| true | ||
| } | ||
| Event::MouseOut => { | ||
| self.hovered = false; | ||
| self.is_mouse_down = false; | ||
| true | ||
| } | ||
| _ => false, | ||
| } | ||
| } else { | ||
| false | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| pub mod button; | ||
| pub mod pane; | ||
| pub mod text; |
Uh oh!
There was an error while loading. Please reload this page.