summaryrefslogtreecommitdiff
path: root/pixel-client/src/ui.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pixel-client/src/ui.rs')
-rw-r--r--pixel-client/src/ui.rs169
1 files changed, 169 insertions, 0 deletions
diff --git a/pixel-client/src/ui.rs b/pixel-client/src/ui.rs
new file mode 100644
index 00000000..802c8e2f
--- /dev/null
+++ b/pixel-client/src/ui.rs
@@ -0,0 +1,169 @@
+use crate::render::{sprite::SpriteDraw, Renderer};
+use hurrycurry_protocol::glam::{IVec2, Vec2};
+use sdl2::{
+ keyboard::{KeyboardState, Keycode, Scancode},
+ mouse::MouseState,
+};
+
+#[derive(Default)]
+pub struct FocusDevice {
+ focus: usize,
+ pressing: Option<usize>,
+ interact_just_pressed: bool,
+ interact_just_released: bool,
+ interact_down: bool,
+}
+
+#[derive(Default)]
+pub struct UiState {
+ mouse_position: Vec2,
+ ui_scale: Vec2,
+
+ keyboard_focus: FocusDevice,
+ mouse_focus: FocusDevice,
+}
+
+pub struct Ui<'a, 'b> {
+ cursor: Vec2,
+ size: Vec2,
+ cross_height: f32,
+ index: usize,
+ direction_horizontal: bool,
+ renderer: &'a mut Renderer<'b>,
+ state: &'a mut UiState,
+}
+
+impl UiState {
+ pub fn update(&mut self, keyboard: &KeyboardState, mouse: &MouseState, _dt: f32) {
+ self.mouse_position = IVec2::new(mouse.x(), mouse.y()).as_vec2() / self.ui_scale;
+
+ self.mouse_focus.update(mouse.left());
+ self.keyboard_focus
+ .update(keyboard.is_scancode_pressed(Scancode::Space));
+ }
+ pub fn keyboard_event(&mut self, keycode: Keycode, down: bool) {
+ if down {
+ match keycode {
+ Keycode::DOWN => self.keyboard_focus.focus += 1,
+ Keycode::UP if self.keyboard_focus.focus > 0 => self.keyboard_focus.focus -= 1,
+ _ => (),
+ }
+ }
+ }
+
+ pub fn draw(&mut self, renderer: &mut Renderer, ui: impl FnOnce(&mut Ui)) {
+ self.ui_scale = renderer.ui_scale;
+ self.mouse_focus.focus = usize::MAX;
+ let mut u = Ui {
+ cursor: Vec2::ZERO,
+ direction_horizontal: false,
+ size: renderer.ui_size,
+ renderer,
+ state: self,
+ cross_height: 0.,
+ index: 0,
+ };
+ ui(&mut u);
+
+ if self.mouse_focus.interact_just_released {
+ self.mouse_focus.pressing = None;
+ }
+ if self.keyboard_focus.interact_just_released {
+ self.keyboard_focus.pressing = None;
+ }
+ }
+}
+
+impl FocusDevice {
+ pub fn update(&mut self, interact: bool) {
+ self.interact_just_pressed = interact && !self.interact_down;
+ self.interact_just_released = !interact && self.interact_down;
+ self.interact_down = interact;
+ }
+ pub fn element(&mut self, index: usize) -> (bool, bool, bool) {
+ let focus = self.focus == index;
+ if focus && self.interact_just_pressed {
+ self.pressing = Some(index)
+ };
+ let pressing = self.pressing == Some(index);
+ let released = self.interact_just_released && pressing && focus;
+ (focus, pressing, released)
+ }
+}
+
+impl<'a, 'b> Ui<'a, 'b> {
+ pub fn text(&mut self, text: &str) {
+ let margin = Vec2::splat(2.);
+ let size = margin + self.renderer.draw_text(self.cursor + margin, text) + margin;
+ self.advance(size);
+ }
+ pub fn button(&mut self, w: f32, label: &str) -> bool {
+ let c = self.cursor;
+ let margin = Vec2::splat(4.);
+ let text_size = self.renderer.draw_text(self.cursor + margin, label);
+ let size = margin + Vec2::new(w, text_size.y) + margin;
+
+ self.index += 1;
+
+ let mouse_rel = self.state.mouse_position - c;
+ if mouse_rel.x >= 0. && mouse_rel.y >= 0. && mouse_rel.x < size.x && mouse_rel.y < size.y {
+ self.state.mouse_focus.focus = self.index;
+ }
+
+ let (focus, pressing, released) = {
+ let (mfocus, mpressing, mreleased) = self.state.mouse_focus.element(self.index);
+ let (kfocus, kpressing, kreleased) = self.state.keyboard_focus.element(self.index);
+ (
+ mfocus || kfocus,
+ mpressing || kpressing,
+ mreleased || kreleased,
+ )
+ };
+
+ let l = if pressing {
+ 100
+ } else if focus {
+ 50
+ } else {
+ 30
+ };
+ self.renderer.draw_ui(SpriteDraw::screen(
+ self.renderer.misc_textures.solid,
+ i32::MAX - 1,
+ c,
+ size,
+ Some([l, l, l, 200]),
+ ));
+
+ self.advance(size);
+ released
+ }
+
+ pub fn fill(&mut self) {
+ self.renderer.draw_ui(SpriteDraw::screen(
+ self.renderer.misc_textures.solid,
+ i32::MAX - 1,
+ self.cursor,
+ self.get_remaining(),
+ Some([30, 30, 30, 200]),
+ ));
+ }
+
+ pub fn get_remaining(&self) -> Vec2 {
+ if self.direction_horizontal {
+ Vec2::new(self.size.x - self.cursor.x, self.cross_height)
+ } else {
+ Vec2::new(self.cross_height, self.size.y - self.cursor.y)
+ }
+ }
+
+ pub fn advance(&mut self, size: Vec2) {
+ if self.direction_horizontal {
+ self.cursor.x += size.x;
+ self.cross_height = self.cross_height.max(size.y);
+ } else {
+ self.cursor.y += size.y;
+ self.cross_height = self.cross_height.max(size.x);
+ }
+ }
+}