This commit is contained in:
griffi-gh 2023-11-21 14:08:22 +01:00
parent 6eb7a3f690
commit ad61b7db94
20 changed files with 283 additions and 122 deletions

21
Cargo.lock generated
View file

@ -78,6 +78,15 @@ version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.7" version = "0.3.7"
@ -724,6 +733,7 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42218cb640844e3872cc3c153dc975229e080a6c4733b34709ef445610550226" checksum = "42218cb640844e3872cc3c153dc975229e080a6c4733b34709ef445610550226"
dependencies = [ dependencies = [
"approx",
"serde", "serde",
] ]
@ -972,6 +982,7 @@ dependencies = [
"image", "image",
"kubi-logging", "kubi-logging",
"kubi-shared", "kubi-shared",
"kubi-ui",
"log", "log",
"lz4_flex", "lz4_flex",
"ndk", "ndk",
@ -1043,6 +1054,16 @@ dependencies = [
"strum", "strum",
] ]
[[package]]
name = "kubi-ui"
version = "0.0.0"
dependencies = [
"glam",
"glium",
"hashbrown 0.14.0",
"nohash-hasher",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"

View file

@ -1,5 +1,5 @@
[workspace] [workspace]
members = ["kubi", "kubi-server", "kubi-shared", "kubi-logging", "kubi-pool"] members = ["kubi", "kubi-server", "kubi-shared", "kubi-logging", "kubi-pool", "kubi-ui"]
default-members = ["kubi"] default-members = ["kubi"]
resolver = "2" resolver = "2"

16
kubi-ui/Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "kubi-ui"
version = "0.0.0"
edition = "2021"
publish = false
[dependencies]
hashbrown = "0.14"
nohash-hasher = "0.2"
glam = { version = "0.24", features = ["approx"] }
glium = { git = "https://github.com/glium/glium", rev = "5d50e7", optional = true }
[features]
default = ["backend_glium", "builtin_elements"]
backend_glium = ["dep:glium"]
builtin_elements = []

2
kubi-ui/src/backend.rs Normal file
View file

@ -0,0 +1,2 @@
#[cfg(feature = "backend_glium")]
pub mod glium;

View file

22
kubi-ui/src/draw.rs Normal file
View file

@ -0,0 +1,22 @@
use glam::{Vec2, vec2, Vec4};
#[derive(Clone, Copy, Debug)]
pub enum UiDrawCall {
///Filled, colored rectangle
Rectangle {
///Position in pixels
position: Vec2,
///Size in pixels
size: Vec2,
///Color (RGBA)
color: Vec4,
}
}
pub struct UiDrawCalls {
pub calls: Vec<UiDrawCall>,
}
pub struct UiDrawPlan {
}

21
kubi-ui/src/element.rs Normal file
View file

@ -0,0 +1,21 @@
use std::any::Any;
use crate::{
LayoutInfo,
draw::UiDrawCall,
measure::{IsMeasurable, Response},
state::StateRepo
};
#[cfg(feature = "builtin_elements")] pub mod progress_bar;
#[cfg(feature = "builtin_elements")] pub mod layout_box;
pub trait UiElement {
fn name(&self) -> &'static str { "UiElement" }
fn state_id(&self) -> Option<u64> { None }
fn is_stateful(&self) -> bool { self.state_id().is_some() }
fn is_stateless(&self) -> bool { self.state_id().is_none() }
fn init_state(&self) -> Option<Box<dyn Any>> { None }
fn is_measurable(&self) -> IsMeasurable { IsMeasurable::No }
fn measure(&self, state: &StateRepo, layout: &LayoutInfo) -> Option<Response> { None }
fn process(&self, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) -> Response;
}

View file

@ -0,0 +1,29 @@
use std::any::Any;
use crate::{UiDirection, LayoutInfo, draw::UiDrawCall, measure::{IsMeasurable, Response}, state::StateRepo};
use super::UiElement;
pub struct LayoutBox {
pub direction: UiDirection,
pub gap: f32,
pub elements: Vec<Box<dyn UiElement>>,
}
impl UiElement for LayoutBox {
fn is_measurable(&self) -> IsMeasurable {
IsMeasurable::Maybe
}
fn measure(&self, state: StateRepo, layout: &LayoutInfo) -> Option<Response> {
for element in &self.elements {
if element.is_measurable() == IsMeasurable::No {
return None
}
element.measure(None, layout);
}
todo!()
}
fn process(&self, _state: Option<&mut Box<dyn Any>>, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) -> Response {
todo!()
}
}

View file

@ -0,0 +1,58 @@
use glam::{vec2, Vec2, Vec4};
use crate::{
UiSize, LayoutInfo,
draw::UiDrawCall,
measure::{Response, IsMeasurable},
state::StateRepo
};
use super::UiElement;
struct ProgressBar {
size: (UiSize, UiSize),
value: f32,
color_foreground: Vec4,
color_background: Vec4,
}
const BAR_HEIGHT: f32 = 20.0;
impl UiElement for ProgressBar {
fn name(&self) -> &'static str { "Progress bar" }
fn is_measurable(&self) -> IsMeasurable { IsMeasurable::Yes }
fn measure(&self, _: &StateRepo, layout: &LayoutInfo) -> Option<Response> {
Some(Response {
size: Vec2::new(
match self.size.0 {
UiSize::Auto => layout.max_size.x,
UiSize::Percentage(p) => layout.max_size.x * p,
UiSize::Pixels(p) => p,
},
match self.size.1 {
UiSize::Auto => BAR_HEIGHT,
UiSize::Percentage(p) => layout.max_size.y * p,
UiSize::Pixels(p) => p,
}
)
})
}
fn process(&self, state: &mut StateRepo, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) -> Response {
let measure = self.measure(&state, layout).unwrap();
draw.push(UiDrawCall::Rectangle {
position: layout.position,
size: measure.size,
color: self.color_background
});
draw.push(UiDrawCall::Rectangle {
position: layout.position,
size: measure.size * vec2(self.value, 1.0),
color: self.color_foreground
});
measure
}
}

10
kubi-ui/src/event.rs Normal file
View file

@ -0,0 +1,10 @@
use glam::Vec2;
pub enum UiEvent {
MouseMove(Vec2),
MouseDown(Vec2),
MouseUp(Vec2),
KeyDown(u32),
KeyUp(u32),
TextInput(char),
}

52
kubi-ui/src/lib.rs Normal file
View file

@ -0,0 +1,52 @@
use std::collections::VecDeque;
use glam::Vec2;
pub mod element;
pub mod event;
pub mod draw;
pub mod backend;
pub mod measure;
pub mod state;
use state::StateRepo;
pub struct KubiUi {
mouse_position: Vec2,
stateful_state: StateRepo,
event_queue: VecDeque<event::UiEvent>,
}
impl KubiUi {
pub fn new() -> Self {
KubiUi {
mouse_position: Vec2::ZERO,
stateful_state: StateRepo::default(),
event_queue: VecDeque::new(),
}
}
}
impl Default for KubiUi {
fn default() -> Self {
Self::new()
}
}
pub enum UiSize {
Auto,
Percentage(f32),
Pixels(f32),
}
#[derive(Default)]
pub enum UiDirection {
#[default]
Vertical,
Horizontal,
}
struct LayoutInfo {
position: Vec2,
max_size: Vec2,
direction: UiDirection,
}

13
kubi-ui/src/measure.rs Normal file
View file

@ -0,0 +1,13 @@
use glam::Vec2;
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum IsMeasurable {
#[default]
No,
Maybe,
Yes
}
pub struct Response {
pub size: Vec2
}

9
kubi-ui/src/state.rs Normal file
View file

@ -0,0 +1,9 @@
use hashbrown::{HashMap, HashSet};
use nohash_hasher::BuildNoHashHasher;
use std::any::Any;
#[derive(Default)]
pub struct StateRepo {
state: HashMap<u64, Box<dyn Any>, BuildNoHashHasher<u64>>,
active_ids: HashSet<u64, BuildNoHashHasher<u64>>
}

View file

@ -11,6 +11,7 @@ crate-type = ["lib", "cdylib"]
[dependencies] [dependencies]
kubi-shared = { path = "../kubi-shared" } kubi-shared = { path = "../kubi-shared" }
kubi-logging = { path = "../kubi-logging" } kubi-logging = { path = "../kubi-logging" }
kubi-ui = { path = "../kubi-ui" }
log = "0.4" log = "0.4"
glium = { git = "https://github.com/glium/glium", rev = "5d50e7" } glium = { git = "https://github.com/glium/glium", rev = "5d50e7" }
glam = { version = "0.24", features = ["debug-glam-assert", "fast-math"] } glam = { version = "0.24", features = ["debug-glam-assert", "fast-math"] }

View file

@ -1,92 +0,0 @@
use glam::{Vec2, vec2, Vec4};
use shipyard::Unique;
pub enum UiSize {
Auto,
Percentage(f32),
Pixels(f32),
}
struct LayoutInfo {
position: Vec2,
max_preferred_size: Vec2,
}
struct Response {
size: Vec2
}
pub trait UiElement {
fn process(&self, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) -> Response;
fn measure(&self, layout: &LayoutInfo) -> Option<Response> { None }
}
pub enum LayoutDirection {
Horizontal,
Vertical
}
pub struct LayoutBox {
pub direction: LayoutDirection,
pub gap: f32,
pub elements: Vec<Box<dyn UiElement>>,
}
struct ProgressBar {
size: (UiSize, UiSize),
value: f32,
color_foreground: Vec4,
color_background: Vec4,
}
const BAR_HEIGHT: f32 = 20.0;
impl UiElement for ProgressBar {
fn measure(&self, layout: &LayoutInfo) -> Option<Response> {
let width = match self.size.0 {
UiSize::Auto => layout.max_preferred_size.x,
UiSize::Percentage(p) => layout.max_preferred_size.x * p,
UiSize::Pixels(p) => p,
};
let height = match self.size.1 {
UiSize::Auto => BAR_HEIGHT,
UiSize::Percentage(p) => layout.max_preferred_size.y * p,
UiSize::Pixels(p) => p,
};
let size = Vec2::new(width, height);
Some(Response { size })
}
fn process(&self, layout: &LayoutInfo, draw: &mut Vec<UiDrawCall>) -> Response {
let measure = self.measure(layout).unwrap();
draw.push(UiDrawCall::Rectangle {
position: layout.position,
size: measure.size,
color: self.color_background
});
draw.push(UiDrawCall::Rectangle {
position: layout.position,
size: measure.size * vec2(self.value, 1.0),
color: self.color_foreground
});
measure
}
}
enum UiDrawCall {
Rectangle {
///Position in pixels
position: Vec2,
///Size in pixels
size: Vec2,
///Color (RGBA)
color: Vec4,
}
}
#[derive(Unique)]
struct UiDrawCalls {
pub calls: Vec<UiDrawCall>,
}

View file

@ -29,8 +29,7 @@ pub(crate) mod delta_time;
pub(crate) mod cursor_lock; pub(crate) mod cursor_lock;
pub(crate) mod control_flow; pub(crate) mod control_flow;
pub(crate) mod state; pub(crate) mod state;
pub(crate) mod gui; pub(crate) mod legacy_gui;
pub(crate) mod gui_v2;
pub(crate) mod networking; pub(crate) mod networking;
pub(crate) mod init; pub(crate) mod init;
pub(crate) mod color; pub(crate) mod color;
@ -77,7 +76,7 @@ use control_flow::{exit_on_esc, insert_control_flow_unique, SetControlFlow};
use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting}; use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting};
use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer}; use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
use init::initialize_from_args; use init::initialize_from_args;
use gui::{render_gui, init_gui, update_gui}; use legacy_gui::{render_gui, init_gui, update_gui};
use loading_screen::update_loading_screen; use loading_screen::update_loading_screen;
use connecting_screen::switch_to_loading_if_connected; use connecting_screen::switch_to_loading_if_connected;
use fixed_timestamp::init_fixed_timestamp_storage; use fixed_timestamp::init_fixed_timestamp_storage;

View file

@ -5,7 +5,7 @@ use crate::{
world::ChunkStorage, world::ChunkStorage,
state::{GameState, NextState, is_changing_state}, state::{GameState, NextState, is_changing_state},
transform::Transform2d, transform::Transform2d,
gui::{ legacy_gui::{
GuiComponent, GuiComponent,
progressbar::ProgressbarComponent progressbar::ProgressbarComponent
}, },