Skip to content
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

Lokui: GUI framework experiment #5

Draft
wants to merge 49 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
468dc30
I think I'm on to something there
Speykious Apr 15, 2023
6054eef
I got one button inside a panel with paddings 👀
Speykious Apr 16, 2023
cb469f6
Got events to work with the whole system too
Speykious Apr 16, 2023
3a73fb4
Turn playground into counter example
Speykious Apr 17, 2023
432a955
Do not print while drawing
Speykious Apr 17, 2023
acd189a
Stop printing while solving layout
Speykious Apr 17, 2023
502494e
Add `debug` function to `Widget` trait
Speykious Apr 17, 2023
6ebb16a
Simplify `Anchor`
Speykious Apr 17, 2023
38d06ca
flex
Speykious Apr 17, 2023
ed737bb
Remove other useless prints
Speykious Apr 17, 2023
e9dc989
Get anchor-origin system right
Speykious Apr 17, 2023
b88dd1e
Consistency in placement of constructor functions
Speykious Apr 17, 2023
25fce4f
Implement text rendering
Speykious Apr 17, 2023
ad39404
Implement `Display` for `Lazy<T>`
Speykious Apr 17, 2023
bd6c2b9
Implement basics of flex layout
Speykious Apr 17, 2023
29e6b54
Add a few comments
Speykious Apr 17, 2023
4bd9b0e
Add `debug` for `Text` component
Speykious Apr 17, 2023
46d59e4
Make `Widget::draw` take a Skia canvas
Speykious Apr 17, 2023
008451f
small format and clean
Speykious Apr 17, 2023
8726f8b
That wasn't needed
Speykious Apr 17, 2023
73d659e
Make lokinit a dev dependency
Speykious Apr 18, 2023
f5a05dc
Put buttons on true center
Speykious Apr 18, 2023
a1350ff
`FlexLayout`'s `align` property has no effect
Speykious Apr 18, 2023
d1174ba
Remove `PaneChild` struct
Speykious Apr 19, 2023
fee22f3
Put flex layout solving into a single function
Speykious Apr 19, 2023
045e040
Add flex layout gap logic
Speykious Apr 19, 2023
cc6e3b6
refactor: functions to solve width and height
Speykious Apr 19, 2023
e8fa294
fix: cargo fmt
Speykious Apr 19, 2023
6b34435
Handle hover events
Speykious Apr 19, 2023
547580c
Basics of animation system
Speykious Apr 21, 2023
241aad8
Woops, didn't mean to change that
Speykious Apr 21, 2023
1b36d4f
Add `WithBackground` widget wrapper
Speykious Apr 22, 2023
7edfde9
Add a `WidgetContainer` trait
Speykious Apr 24, 2023
a927665
Remove `Laz`
Speykious Apr 24, 2023
289f752
Remove unused imports
Speykious Apr 24, 2023
6393c0d
Use `get_mut()` in example
Speykious Apr 24, 2023
1aed813
Remove now useless `set` method for `Lazy`
Speykious Apr 24, 2023
27f1aed
Make padding a wrapper
Speykious Apr 25, 2023
b2a4f5f
Slightly decrease verbosity w/ color in RectState
Speykious Apr 25, 2023
58f247d
Rename `FlexLayout` to `Flex`
Speykious Apr 25, 2023
363a9f0
Introduce a prelude module for ease of imports
Speykious Apr 25, 2023
9a7247a
Actually making padding a wrapper was a bad idea
Speykious Apr 25, 2023
8fcefa3
Add WithOnCLick and WithOnEvent wrappers
Speykious May 14, 2023
cf11755
Make `on_click` callback return a boolean
Speykious May 14, 2023
3845d90
Add `argb_hex` method to `Color`
Speykious May 14, 2023
6b6cb6e
Make counter example look slightly better
Speykious May 14, 2023
3fdf34b
Replace old lokinit code with winit for now
Speykious Jul 26, 2023
bbd32b9
Ah yes, the up is made out of down
Speykious Jul 27, 2023
6508348
Fix Skia drop order bug and simplify example
Speykious Jul 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
773 changes: 773 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions lokui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
skia-safe = { version = "0.60.0", features = ["gl"] }

[dev-dependencies]
miniquad = { git = "https://github.com/loki-chat/lokinit" }
161 changes: 161 additions & 0 deletions lokui/examples/counter.rs
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);
AshesOfEther marked this conversation as resolved.
Show resolved Hide resolved

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.
Show resolved Hide resolved

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.
Show resolved Hide resolved
.child(
AshesOfEther marked this conversation as resolved.
Show resolved Hide 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.
Show resolved Hide resolved
.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),
),
Copy link
Member

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lol I didn't bother not repeating. xD
It's also not exactly the same thing. One is a text inside a pane, one is a +1 button, the other is a -1 button. The thing that could avoid some repetition here at best would be the button layouts, because they are the exact same... Which would make it slightly better since the with_layout bit takes the most space I guess :v

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, layout is what I meant to be referring to.

Copy link
Member Author

Choose a reason for hiding this comment

The 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,
})
},
);
}
139 changes: 139 additions & 0 deletions lokui/src/components/button.rs
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
}
}
}
3 changes: 3 additions & 0 deletions lokui/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod button;
pub mod pane;
pub mod text;
Loading