207 lines
4.8 KiB
Rust
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;
|
|
}
|
|
}
|