#![allow(clippy::new_ret_no_self)]
#![feature(trait_alias)]
use std::{
collections::HashMap,
fmt::Debug,
ops::{Deref, DerefMut},
};
use base::{Empty, FloatingWindow};
use glium::winit::event::MouseButton;
use sge_color::Color;
use sge_input::{Input, get_input};
use sge_macros::{gen_ref_type, include_texture};
use sge_textures::{ImageFormat, TextureRef, load_texture};
use sge_time::{delta_time, frame_count, time};
use sge_types::Area;
use sge_vectors::Vec2;
use sge_window::window_size;
pub mod base;
pub mod library;
pub mod prelude;
sge_global::global!(Textures, ui_textures);
struct Textures {
close: TextureRef,
minimise: TextureRef,
}
pub use sge_rng::id;
pub struct UiStorage {
states: HashMap<StateRef, SomeState>,
elements_interacted: HashMap<usize, usize>,
elements_interacted_this_frame: Vec<usize>,
windows: Vec<*const FloatingWindow>,
}
sge_global::global!(UiStorage, ui_storage);
pub fn init_ui() {
let close = include_texture!("../assets/textures/close.png");
let minimise = include_texture!("../assets/textures/minimise.png");
set_ui_textures(Textures { close, minimise });
init_ui_nodes_storage();
set_ui_storage(UiStorage {
states: HashMap::new(),
elements_interacted: HashMap::new(),
elements_interacted_this_frame: Vec::new(),
windows: Vec::new(),
});
log::info!("Initialized sge_ui");
update();
}
pub struct UiState {
frame: usize,
delta_time: f32,
time: f32,
}
impl UiState {
fn new() -> Self {
Self {
frame: frame_count(),
delta_time: delta_time(),
time: time(),
}
}
pub fn input(&self) -> &'static Input {
get_input()
}
pub fn frame(&self) -> usize {
self.frame
}
pub fn delta_time(&self) -> f32 {
self.delta_time
}
pub fn time(&self) -> f32 {
self.time
}
pub fn is_hovered(&self, area: Area) -> bool {
self.input().is_cursor_within_area(area)
}
pub fn is_active(&self, area: Area) -> bool {
self.input().is_cursor_within_area(area) && self.input().mouse_held(MouseButton::Left)
}
pub fn is_clicked(&self, area: Area) -> bool {
self.input().is_cursor_within_area(area) && self.input().mouse_released(MouseButton::Left)
}
}
pub struct SomeNode {
node: Box<dyn UiNode>,
}
impl Deref for SomeNode {
type Target = dyn UiNode;
fn deref(&self) -> &Self::Target {
&*self.node
}
}
impl DerefMut for SomeNode {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.node
}
}
gen_ref_type!(SomeNode, UiRef, ui_nodes);
pub trait UiNode: Debug {
fn preferred_dimensions(&self) -> Vec2;
fn draw(&self, area: Area, ui: &UiState) -> Vec2;
#[allow(unused_variables)]
fn size(&self, area: Area) -> Vec2 {
self.preferred_dimensions()
}
}
pub trait ToGenericUiNode: UiNode + Sized {
fn to_generic(self) -> SomeNode;
fn to_ref(self) -> UiRef {
self.to_generic().create()
}
}
impl<T: UiNode + 'static> ToGenericUiNode for T {
fn to_generic(self) -> SomeNode {
SomeNode {
node: Box::new(self),
}
}
}
pub(crate) type Child = UiRef;
impl Default for UiRef {
fn default() -> Self {
base::EMPTY
}
}
pub fn update() {
for window in get_ui_storage().windows.drain(..) {
let window = unsafe { &*window };
window.actually_draw(&UiState::new());
}
get_ui_nodes_state().clear();
get_ui_nodes_state().push(Empty.to_generic());
get_ui_storage().elements_interacted_this_frame.clear();
}
pub fn draw_ui_unbounded(node: UiRef, position: Vec2) -> Vec2 {
let area = Area {
top_left: position,
size: Vec2::INFINITY,
};
draw_ui_in_area(node, area)
}
pub fn draw_ui(node: UiRef, position: Vec2) -> Vec2 {
let area = Area {
top_left: position,
size: window_size() - position,
};
draw_ui_in_area(node, area)
}
pub fn draw_ui_window(node: UiRef) -> Vec2 {
let area = Area {
top_left: Vec2::ZERO,
size: window_size(),
};
draw_ui_in_area(node, area)
}
pub fn draw_ui_in_area(node: UiRef, area: Area) -> Vec2 {
let state = UiState::new();
node.node.draw(area, &state)
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct State<T: Debug> {
_ref: StateRef,
_marker: std::marker::PhantomData<T>,
}
impl<T: Debug + 'static> Debug for State<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.get().fmt(f)
}
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct StateRef(usize);
impl<T: Debug> State<T> {
pub fn from_id(id: usize) -> Self {
Self {
_ref: StateRef(id),
_marker: std::marker::PhantomData,
}
}
pub fn get_or_create_mut<F>(&self, create: F) -> &'static mut T
where
F: FnOnce() -> T,
{
let storage = &mut get_ui_storage().states;
let some_state = &mut storage.get_mut(&self._ref);
if let Some(state) = some_state
&& state.state.downcast_ref::<T>().is_none()
{
let new_state = create();
state.state = Box::new(new_state);
} else if some_state.is_none() {
storage.insert(
self._ref,
SomeState {
state: Box::new(create()),
},
);
}
storage
.get_mut(&self._ref)
.unwrap()
.state
.downcast_mut::<T>()
.unwrap()
}
pub fn get(&self) -> Option<&'static T> {
let storage = &get_ui_storage().states;
let some_state = storage.get(&self._ref)?;
some_state.state.downcast_ref::<T>()
}
pub fn get_mut(&self) -> Option<&'static mut T> {
let storage = &mut get_ui_storage().states;
let some_state = storage.get_mut(&self._ref)?;
some_state.state.downcast_mut::<T>()
}
}
impl<T: Default + Debug> State<T> {
pub fn get_or_default(&self) -> &'static mut T {
self.get_or_create_mut(T::default)
}
}
pub struct SomeState {
state: Box<dyn std::any::Any>,
}
pub fn all_elements_interacted_this_frame() -> &'static [usize] {
&get_ui_storage().elements_interacted_this_frame
}