use nalgebra::Vector2; use rhai::{Dynamic, ImmutableString}; use std::{cell::RefCell, collections::HashMap, rc::Rc}; use winit::window::Window; use super::{super::api::Rect, OwnedTextArea}; use crate::{ui::UiElement, InputEvent, RenderInput, RenderState}; #[derive(Debug)] pub struct UiScrollbox { pub name: ImmutableString, pub rect: Rect, pub offset: Vector2, pub elements: HashMap>>, has_mouse: bool, } impl UiScrollbox { pub fn new(name: ImmutableString, rect: Rect) -> Self { Self { name, rect, elements: HashMap::new(), offset: Vector2::new(0.0, 0.0), has_mouse: false, } } pub fn add_element(&mut self, e: Rc>) { let name = e.borrow().get_name().clone(); self.elements.insert(name, e); } pub fn remove_element(&mut self, sprite: &ImmutableString) { self.elements.remove(sprite); } pub fn step(&mut self, t: f32) { for (_name, e) in &self.elements { match &mut *e.clone().borrow_mut() { UiElement::Sprite(sprite) => sprite.step(t), UiElement::RadialBar(_) => {} UiElement::Text(..) => {} UiElement::Scrollbox(..) => {} UiElement::SubElement { .. } => {} } } } pub fn handle_event( &mut self, input: &RenderInput, state: &mut RenderState, event: &InputEvent, ) -> Option { let r = self .rect .to_centered(&state.window, input.ct.config.ui_scale); // TODO: handle only if used in event() // i.e, scrollable sprites shouldn't break scrollboxes // First, check if this event is captured by any sub-elements for (_, e) in &mut self.elements { let arg = match &mut *e.borrow_mut() { UiElement::Sprite(sprite) => sprite.handle_event(&input, state, &event), UiElement::Scrollbox(sbox) => sbox.handle_event(&input, state, &event), UiElement::RadialBar(_) | UiElement::Text(..) => None, // Subelements are intentionally skipped, // they should be handled by their parent's `handle_event` method. UiElement::SubElement { .. } => None, }; if arg.is_some() { return arg; } } // If no inner events were captured, handle self events. match event { InputEvent::MouseMove(pos) => { if r.contains_mouse(state, pos) && !self.has_mouse { self.has_mouse = true; } if !r.contains_mouse(state, pos) && self.has_mouse { self.has_mouse = false; } } InputEvent::Scroll(x) => { if self.has_mouse { self.offset.y -= x; } } _ => return None, } return None; } } impl UiScrollbox { pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) { for (_name, e) in &self.elements { match &*e.clone().borrow() { UiElement::Sprite(sprite) => { sprite.push_to_buffer_with_offset(input, state, self.offset) } UiElement::RadialBar(..) => {} UiElement::Text(..) => {} UiElement::Scrollbox(..) => {} UiElement::SubElement { .. } => {} } } } } // TODO: don't allocate here impl<'a> UiScrollbox { pub fn get_textareas(&'a self, input: &RenderInput, window: &Window) -> Vec { let mut v = Vec::with_capacity(32); for e in self.elements.values() { match &*e.clone().borrow() { UiElement::Text(x) => { v.push(x.get_textarea_with_offset(input, window, self.offset)) } _ => {} } } return v; } }