Mark b170f3f53f
Reworked renderer for Directives
Added OwnedTextArea & reworked textarea creation
Added ScrollBox
2024-02-07 15:58:14 -08:00

207 lines
4.8 KiB
Rust

use galactica_content::Content;
use log::{debug, error};
use rhai::ImmutableString;
use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc, time::Instant};
use winit::window::Window;
use super::{
elements::{FpsIndicator, OwnedTextArea, UiRadialBar, UiScrollbox, UiSprite, UiTextBox},
Camera,
};
use crate::{RenderInput, RenderState};
#[derive(Debug)]
pub enum UiElement {
Sprite(UiSprite),
RadialBar(UiRadialBar),
Text(UiTextBox),
Scrollbox(UiScrollbox),
/// This is a sub-element managed by another element
SubElement {
parent: ImmutableString,
element: Rc<RefCell<UiElement>>,
},
}
impl UiElement {
pub fn get_name(&self) -> ImmutableString {
match self {
Self::Sprite(x) => x.name.clone(),
Self::RadialBar(x) => x.name.clone(),
Self::Text(x) => x.name.clone(),
Self::Scrollbox(x) => x.name.clone(),
Self::SubElement { element, .. } => element.borrow().get_name(),
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct UiConfig {
pub show_phys: bool,
pub show_starfield: bool,
}
pub(crate) struct UiState {
pub elements: HashMap<ImmutableString, UiElement>,
pub names: Vec<ImmutableString>,
pub ct: Arc<Content>,
current_scene: Option<ImmutableString>,
show_timings: bool,
fps_indicator: FpsIndicator,
last_step: Instant,
pub config: UiConfig,
/// The player's camera.
/// Only used when drawing physics.
pub camera: Camera,
}
// TODO: remove this
unsafe impl Send for UiState {}
unsafe impl Sync for UiState {}
impl UiState {
pub fn new(ct: Arc<Content>, state: &mut RenderState) -> Self {
Self {
ct,
elements: HashMap::new(),
names: Vec::new(),
current_scene: None,
show_timings: true,
fps_indicator: FpsIndicator::new(state),
config: UiConfig {
show_phys: false,
show_starfield: false,
},
last_step: Instant::now(),
camera: Camera::new(),
}
}
pub fn clear(&mut self) {
self.elements.clear();
self.names.clear();
}
pub fn len(&self) -> usize {
self.elements.len()
}
/*
pub fn get_by_idx(&self, idx: usize) -> Option<&UiElement> {
self.elements.get(idx)
}
pub fn get_by_name(&self, name: &ImmutableString) -> Option<&UiElement> {
let idx = self.names.get(name);
if idx.is_none() {
return None;
}
self.get_by_idx(*idx.unwrap())
}
*/
pub fn get_mut_by_idx(&mut self, idx: usize) -> Option<&mut UiElement> {
let name = self.names.get(idx);
if name.is_none() {
return None;
}
self.elements.get_mut(name.unwrap())
}
pub fn get_mut_by_name(&mut self, name: &ImmutableString) -> Option<&mut UiElement> {
self.elements.get_mut(name)
}
pub fn get_scene(&self) -> &Option<ImmutableString> {
&self.current_scene
}
pub fn set_scene(&mut self, scene: ImmutableString) {
if !self.ct.config.ui_scenes.contains_key(scene.as_str()) {
error!("tried to switch to ui scene `{scene}`, which doesn't exist");
return;
}
debug!("switching to {:?}", scene);
self.current_scene = Some(scene);
}
pub fn step(&mut self, state: &mut RenderState, input: Arc<RenderInput>) {
let t = self.last_step.elapsed().as_secs_f32();
for (_, e) in &mut self.elements {
match e {
UiElement::Sprite(sprite) => sprite.step(t),
UiElement::Scrollbox(sbox) => sbox.step(t),
_ => {}
}
}
if self.show_timings {
self.fps_indicator
.step(&input, &mut state.text_font_system.borrow_mut());
}
self.last_step = Instant::now();
}
pub fn add_element(&mut self, e: UiElement) {
self.names.push(e.get_name().clone());
self.elements.insert(e.get_name().clone(), e);
}
// Remove an element from this sprite.
// This does NOT remove subelements from their parent sprites.
pub fn remove_element_incomplete(&mut self, name: &ImmutableString) -> Option<UiElement> {
let e = self.elements.remove(name);
self.names.retain(|x| *x != name);
return e;
}
// Remove an element from this sprite and from all subsprites.
pub fn remove_element(&mut self, name: &ImmutableString) {
let e = self.elements.remove(name);
self.names.retain(|x| *x != name);
match e {
Some(UiElement::SubElement { parent, element }) => {
let x = Rc::into_inner(element).unwrap().into_inner();
let parent = self.elements.get_mut(&parent).unwrap();
match parent {
UiElement::Scrollbox(s) => s.remove_element(&x.get_name()),
_ => unreachable!("invalid subelement parent"),
}
}
_ => {}
}
}
}
// TODO: don't allocate here, return an iterator
impl<'a> UiState {
pub fn get_textareas(&'a self, input: &RenderInput, window: &Window) -> Vec<OwnedTextArea> {
let mut v = Vec::with_capacity(32);
if self.current_scene.is_none() {
return v;
}
if self.show_timings {
v.push(self.fps_indicator.get_textarea(input, window))
}
for e in self.elements.values() {
match &e {
UiElement::Text(t) => v.push(t.get_textarea(input, window)),
UiElement::Scrollbox(b) => v.extend(b.get_textareas(input, window)),
_ => {}
}
}
return v;
}
}