~saiko/game

09063b0e2d94aedb628da97caee0fdad8950d39e — 2xsaiko 1 year, 11 days ago 1ace5c9
It does the thing.
M src/cmd/mod.rs => src/cmd/mod.rs +8 -0
@@ 324,4 324,12 @@ impl ConVar {
      _ => None
    }
  }

  pub fn get_bool_value(&self) -> bool {
    match self {
      ConVar::Number { value, .. } => value.get() != 0.0,
      ConVar::Integer { value, .. } => value.get() != 0,
      _ => false,
    }
  }
}

M src/main.rs => src/main.rs +27 -18
@@ 45,6 45,7 @@ use crate::math::*;
use crate::res::{DataSource, ResourceLoader};
use crate::res::merge::MergeModeTable;
use crate::shader::ShaderManager;
use crate::testui::{TestUi, TestUiController};
use crate::ui::graphics::{Alignment, Graphics, StringDrawProps};
use crate::ui::host::UiHost;
use crate::util::{AnySurface, Color, LogPipe};


@@ 153,6 154,8 @@ fn start_game(env: Environment) -> ! {
      .with_command("quit", box command(move |_, _, _| { running.store(false, Ordering::Relaxed); }))
      .with_help_text("quit", "quit the game")
      .with_cvar("fps_limit", ConVar::of_int(120, Some(1), None))
      .with_cvar("debug_info", ConVar::of_bool(false))
      .with_help_text("debug_info", "whether to show debug info like fps and player position")
      .with_command("exec", box expand_command(move |args, _, out| {
        let file = format!("cfg/{}.cfg", *args.get(0)?);



@@ 208,6 211,8 @@ fn start_game(env: Environment) -> ! {
    fr,
  };

  game.ui_host.open_ui(TestUiController::new());

  event_loop.run_return(|event, target, cf| game.handle_event(event, target, cf));
  exit(0);
}


@@ 261,6 266,10 @@ impl Game<'_> {
      }
      Event::WindowEvent { event: WindowEvent::CursorMoved { position, .. }, .. } => {
//          mdiff = Some((position.x - center.x, position.y - center.y));
        self.ui_host.on_mouse_move((position.x * self.window.dpi) as i32, (position.y * self.window.dpi) as i32);
      }
      Event::WindowEvent { event: WindowEvent::MouseInput { device_id, state, button, modifiers }, .. } => {
        self.ui_host.on_click(state, button, modifiers)
      }
      Event::WindowEvent { event: WindowEvent::HiDpiFactorChanged(f), .. } => {
        self.window.on_dpi_changed(f);


@@ 334,25 343,25 @@ impl Game<'_> {

    let fr = self.fr.choose((14, false, window.dpi));

    let s = "Press [tab] to switch views";
    let color = Color::white();
    g.push_matrix(|g| {
      g.translatei(0, 0, 100);
      g.draw_string(s, window.width() as i32 - 2, 2, StringDrawProps::default().with_alignment(Alignment::Right));
      g.draw_string(&format!("fps: {} ({} actual)", fps.smooth_fps(), fps.current_fps()), 2, 2, StringDrawProps::default());

      let camera = camera.borrow();
      g.draw_string(&format!("pos: {:5.3} {:5.3} {:5.3}",
                             PrettyPrintFloat(camera.pos().x() as f64),
                             PrettyPrintFloat(camera.pos().y() as f64),
                             PrettyPrintFloat(camera.pos().z() as f64)),
                    2, 2 + fr.get_height() as i32, StringDrawProps::default());
      g.draw_string(&format!("ang: {:5.3} {:5.3} {:5.3}",
                             PrettyPrintFloat(camera.yaw() as f64),
                             PrettyPrintFloat(camera.pitch() as f64),
                             PrettyPrintFloat(camera.roll() as f64)),
                    2, 2 + 2 * fr.get_height() as i32, StringDrawProps::default());
    });
    if self.ce.get_cvar("debug_info").unwrap().get_bool_value() {
      g.push_matrix(|g| {
        g.translatei(0, 0, 100);
        g.draw_string(&format!("fps: {} ({} actual)", fps.smooth_fps(), fps.current_fps()), 2, 2, StringDrawProps::default());

        let camera = camera.borrow();
        g.draw_string(&format!("pos: {:5.3} {:5.3} {:5.3}",
                               PrettyPrintFloat(camera.pos().x() as f64),
                               PrettyPrintFloat(camera.pos().y() as f64),
                               PrettyPrintFloat(camera.pos().z() as f64)),
                      2, 2 + fr.get_height() as i32, StringDrawProps::default());
        g.draw_string(&format!("ang: {:5.3} {:5.3} {:5.3}",
                               PrettyPrintFloat(camera.yaw() as f64),
                               PrettyPrintFloat(camera.pitch() as f64),
                               PrettyPrintFloat(camera.roll() as f64)),
                      2, 2 + 2 * fr.get_height() as i32, StringDrawProps::default());
      });
    }
    drop(g);
    frame.finish().unwrap();
  }

M src/testui.rs => src/testui.rs +20 -5
@@ 1,12 1,12 @@
use crate::ui::controller::UiController;
use crate::ui::element::Button;
use crate::ui::graphics::Graphics;
use crate::ui::graphics::{Graphics, StringDrawProps};
use crate::ui::layout::{BorderLayout, LayoutManager};
use crate::ui::panel::Container;
use crate::ui::rescap::ResizeCapabilities;

ui_def! {
  impl TestUi, TestUiElements, TestUiController, TestUiAccess {
  pub impl TestUi, TestUiAccess for TestUiElements, TestUiController {
    Button => button,
    Button1 => button1,
    Button2 => button2,


@@ 23,11 23,15 @@ pub struct TestUiElements {
  button4: Container<Button>,
}

pub struct TestUiController;
pub struct TestUiController {
  click_counter: u32,
}

impl TestUiController {
  pub fn new() -> TestUi {
    TestUi::new(TestUiController)
    TestUi::new(TestUiController {
      click_counter: 0
    })
  }

  fn get_layout(&self) -> impl LayoutManager<TestUiElements> {


@@ 55,7 59,13 @@ impl UiController for TestUiController {
    }
  }

  fn update(&mut self, elements: &mut TestUiElements) {}
  fn update(&mut self, elements: &mut TestUiElements) {
    elements.button.poll_events(|e| self.click_counter += 1);
    elements.button1.poll_events(|e| self.click_counter += 1);
    elements.button2.poll_events(|e| self.click_counter += 1);
    elements.button3.poll_events(|e| self.click_counter += 1);
    elements.button4.poll_events(|e| self.click_counter += 1);
  }

  fn resize(&mut self, elements: &mut Self::Elements, g: &Graphics, width: u32, height: u32) {
    self.get_layout().relayout(elements, g, width, height);


@@ 64,4 74,9 @@ impl UiController for TestUiController {
  fn get_resize_capabilities(&self, elements: &Self::Elements, g: &Graphics) -> ResizeCapabilities {
    self.get_layout().get_resize_capabilities(elements, g)
  }

  fn draw(&mut self, elements: &mut Self::Elements, g: &mut Graphics) {
    self.default_draw(elements, g);
    g.draw_string(&format!("click counter: {}", self.click_counter), 2, 2, StringDrawProps::default());
  }
}

M src/ui/controller.rs => src/ui/controller.rs +7 -0
@@ 14,6 14,10 @@ pub trait UiController {
  fn get_resize_capabilities(&self, elements: &Self::Elements, g: &Graphics) -> ResizeCapabilities;

  fn draw(&mut self, elements: &mut Self::Elements, g: &mut Graphics) {
    self.default_draw(elements, g);
  }

  fn default_draw(&mut self, elements: &mut Self::Elements, g: &mut Graphics) {
    use crate::ui::access::Values;

    let all = <Self::Elements as UiAccess>::Access::values();


@@ 27,4 31,7 @@ pub trait UiController {
  fn open(&mut self) {}

  fn close(&mut self) {}

  // TODO implement redraw on demand
  fn is_animated(&self) -> bool { true }
}

M src/ui/element/button.rs => src/ui/element/button.rs +39 -11
@@ 1,3 1,5 @@
use glium::glutin::event::{ElementState, ModifiersState, MouseButton};

use crate::ui::element::Element;
use crate::ui::event::EventQueue;
use crate::ui::graphics::{Alignment, Graphics, StringDrawProps};


@@ 13,6 15,8 @@ pub struct Button {
  text: String,
  width: u32,
  height: u32,
  is_mouse_over: bool,
  is_pressed: bool,
  queue: EventQueue<ButtonEvent>,
}



@@ 22,6 26,8 @@ impl Button {
      text: text.to_owned(),
      width: 0,
      height: 0,
      is_mouse_over: false,
      is_pressed: false,
      queue: EventQueue::new(),
    })
  }


@@ 33,11 39,43 @@ impl Button {

impl Element for Button {
  fn draw(&mut self, g: &mut Graphics) {
    g.draw_rect(1, 1, self.width as i32 - 2, self.height as i32 - 2, Color::from_packed_rgb(0x00ccff));
    let color = match (self.is_mouse_over, self.is_pressed) {
      (false, false) => Color::from_packed_rgb(0x00ccff),
      (_, true) => Color::from_packed_rgb(0xcc00ff),
      (true, false) => Color::from_packed_rgb(0x00eeff),
    };

    g.draw_rect(1, 1, self.width as i32 - 2, self.height as i32 - 2, color);
    g.draw_string(&self.text, self.width as i32 / 2, self.height as i32 / 2 - (g.font_renderer().get_height() / 2.0) as i32,
                  StringDrawProps::default().with_alignment(Alignment::Center).with_color(Color::from_packed_rgb(0x7f7f7f)));
  }

  fn set_size(&mut self, width: u32, height: u32) {
    self.width = width;
    self.height = height;
  }

  fn on_mouse_move(&mut self, mouse_x: i32, mouse_y: i32) {
    let is_in_bounds = mouse_x >= 0 && mouse_x < self.width as i32 && mouse_y >= 0 && mouse_y < self.height as i32;
    self.is_mouse_over = is_in_bounds;
  }

  fn on_click(&mut self, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool {
    let is_in_bounds = mouse_x >= 0 && mouse_x < self.width as i32 && mouse_y >= 0 && mouse_y < self.height as i32;
    if button == MouseButton::Left {
      if is_in_bounds && state == ElementState::Pressed && !self.is_pressed {
        self.is_pressed = true;
        true
      } else if state == ElementState::Released && self.is_pressed {
        self.is_pressed = false;
        if is_in_bounds {
          self.queue.push(ButtonEvent::Pressed);
        }
        true
      } else { false }
    } else { false }
  }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
    let fr = g.font_renderer();
    ResizeCapabilities::new(


@@ 49,14 87,4 @@ impl Element for Button {
      None,
    )
  }

  fn click(&mut self, mouse_x: i32, mouse_y: i32) -> bool {
    self.queue.push(ButtonEvent::Pressed);
    true
  }

  fn set_size(&mut self, width: u32, height: u32) {
    self.width = width;
    self.height = height;
  }
}
\ No newline at end of file

M src/ui/element/mod.rs => src/ui/element/mod.rs +7 -1
@@ 1,3 1,5 @@
use glium::glutin::event::{ElementState, ModifiersState, MouseButton};

pub use button::*;
pub use spacer::*;
pub use viewport::*;


@@ 16,9 18,13 @@ pub trait Element {

  fn set_size(&mut self, width: u32, height: u32);

  fn click(&mut self, mouse_x: i32, mouse_y: i32) -> bool { true }
  fn on_mouse_move(&mut self, mouse_x: i32, mouse_y: i32) {}

  fn on_click(&mut self, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool { true }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities;

  fn is_animated(&self) -> bool { false }
}

pub trait Positionable: Element {

M src/ui/host.rs => src/ui/host.rs +26 -1
@@ 1,3 1,5 @@
use glium::glutin::event::{ElementState, ModifiersState, MouseButton};

use crate::ui::element::Element;
use crate::ui::graphics::Graphics;



@@ 5,6 7,8 @@ pub struct UiHost {
  current_controller: Option<Box<dyn Element>>,
  width: u32,
  height: u32,
  mouse_x: i32,
  mouse_y: i32,
}

impl UiHost {


@@ 13,6 17,8 @@ impl UiHost {
      current_controller: None,
      width: 0,
      height: 0,
      mouse_x: 0,
      mouse_y: 0,
    }
  }



@@ 42,11 48,30 @@ impl UiHost {

  pub fn draw(&mut self, g: &mut Graphics) {
    if let Some(ref mut c) = self.current_controller {
      c.update();
      c.draw(g)
    }
  }

  pub fn is_animated(&self) -> bool {
    true
    if let Some(ref c) = self.current_controller {
      c.is_animated()
    } else {
      false
    }
  }

  pub fn on_mouse_move(&mut self, mouse_x: i32, mouse_y: i32) {
    self.mouse_x = mouse_x;
    self.mouse_y = mouse_y;
    if let Some(ref mut c) = self.current_controller {
      c.on_mouse_move(mouse_x, mouse_y);
    }
  }

  pub fn on_click(&mut self, state: ElementState, button: MouseButton, modifiers: ModifiersState) {
    if let Some(ref mut c) = self.current_controller {
      c.on_click(self.mouse_x, self.mouse_y, state, button, modifiers);
    }
  }
}
\ No newline at end of file

M src/ui/mod.rs => src/ui/mod.rs +80 -52
@@ 1,6 1,8 @@
use crate::ui::access::UiAccess;
use glium::glutin::event::{ElementState, ModifiersState, MouseButton};

use crate::ui::access::{UiAccess, Values};
use crate::ui::controller::UiController;
use crate::ui::element::Element;
use crate::ui::element::{Element, Positionable};
use crate::ui::graphics::Graphics;
use crate::ui::rescap::ResizeCapabilities;



@@ 15,12 17,83 @@ pub mod event;
pub mod host;
pub mod controller;

pub struct GenUi<E: UiAccess, C: UiController> {
  elements: E,
  controller: C,
  width: u32,
  height: u32,
  is_dirty: bool,
}

impl<E: UiAccess + 'static, C: UiController<Elements=E>> GenUi<E, C> {
  pub fn new(controller: C) -> Self {
    let elements = controller.create_elements();
    Self {
      elements,
      controller,
      width: 0,
      height: 0,
      is_dirty: true,
    }
  }
}

impl<E: UiAccess + 'static, C: UiController<Elements=E>> Element for GenUi<E, C> {
  fn update(&mut self) {
    self.controller.update(&mut self.elements);
  }

  fn draw(&mut self, g: &mut Graphics) {
    if self.is_dirty {
      self.controller.resize(&mut self.elements, g, self.width, self.height);
      self.is_dirty = false;
    }
    self.controller.draw(&mut self.elements, g);
  }

  fn set_size(&mut self, width: u32, height: u32) {
    self.width = width;
    self.height = height;
    self.is_dirty = true;
  }

  fn on_mouse_move(&mut self, mouse_x: i32, mouse_y: i32) {
    self.for_each(|e| {
      e.on_mouse_move(mouse_x - e.x(), mouse_y - e.y());
    });
  }

  fn on_click(&mut self, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool {
    self.for_each(|e| {
      e.on_click(mouse_x - e.x(), mouse_y - e.y(), state, button, modifiers);
    });
    true
  }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
    self.controller.get_resize_capabilities(&self.elements, g)
  }

  fn is_animated(&self) -> bool {
    self.controller.is_animated()
  }
}

impl<E: UiAccess + 'static, C: UiController<Elements=E>> GenUi<E, C> {
  fn for_each(&mut self, mut op: impl FnMut(&mut dyn Positionable)) {
    for v in E::Access::values() {
      op(self.elements.get_element_mut(*v))
    }
  }
}

#[macro_export]
macro_rules! ui_def {
  (impl $ui_struct:ident, $el_struct:ty, $ctrl_struct:ty, $acc_struct:ident {
  ($vis:vis impl $ui_struct:ident, $acc_struct:ident for $el_struct:ty, $ctrl_struct:ty {
      $($variant:ident => $field:ident),* $(,)?
    }) => {
      _ui_def_inner!(
        $vis,
        $ui_struct,
        $el_struct,
        $ctrl_struct,


@@ 31,11 104,11 @@ macro_rules! ui_def {
}

macro_rules! _ui_def_inner {
  ($ui_struct:ident, $el_struct:ty, $ctrl_struct:ty, $acc_struct:ident, $($variant:ident => $field:ident),* $(,)?) => {
    type $ui_struct = $crate::ui::GenUi<$el_struct,$ctrl_struct>;
  ($vis:vis, $ui_struct:ident, $el_struct:ty, $ctrl_struct:ty, $acc_struct:ident, $($variant:ident => $field:ident),* $(,)?) => {
    $vis type $ui_struct = $crate::ui::GenUi<$el_struct,$ctrl_struct>;

    #[derive(Copy, Clone, Eq, PartialEq, Debug)]
    pub enum $acc_struct {
    $vis enum $acc_struct {
      $($variant),*
    }



@@ 78,49 151,4 @@ macro_rules! _ui_def_inner {
      }
    }
  };
}

pub struct GenUi<E: UiAccess, C: UiController> {
  elements: E,
  controller: C,
  width: u32,
  height: u32,
  is_dirty: bool,
}

impl<E: UiAccess + 'static, C: UiController<Elements=E>> GenUi<E, C> {
  pub fn new(controller: C) -> Self {
    let elements = controller.create_elements();
    Self {
      elements,
      controller,
      width: 0,
      height: 0,
      is_dirty: true,
    }
  }
}

impl<E: UiAccess + 'static, C: UiController<Elements=E>> Element for GenUi<E, C> {
  fn update(&mut self) {
    self.controller.update(&mut self.elements);
  }

  fn draw(&mut self, g: &mut Graphics) {
    if self.is_dirty {
      self.controller.resize(&mut self.elements, g, self.width, self.height);
      self.is_dirty = false;
    }
    self.controller.draw(&mut self.elements, g);
  }

  fn set_size(&mut self, width: u32, height: u32) {
    self.width = width;
    self.height = height;
    self.is_dirty = true;
  }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
    self.controller.get_resize_capabilities(&self.elements, g)
  }
}
}
\ No newline at end of file

M src/ui/panel.rs => src/ui/panel.rs +26 -30
@@ 1,5 1,7 @@
use std::ops::{Deref, DerefMut};

use glium::glutin::event::{ElementState, ModifiersState, MouseButton};

use crate::ui::element::{Element, Positionable};
use crate::ui::graphics::Graphics;
use crate::ui::rescap::ResizeCapabilities;


@@ 27,31 29,6 @@ impl<T: Element> Container<T> {

  pub fn drawable_mut(&mut self) -> &mut T { &mut self.drawable }

  pub fn x(&self) -> i32 { self.x }

  pub fn y(&self) -> i32 { self.y }

  pub fn width(&self) -> u32 { self.width }

  pub fn height(&self) -> u32 { self.height }

  pub fn draw(&mut self, g: &mut Graphics) {
    g.push_matrix(|g| {
      g.translatei(self.x, self.y, -1);
      self.drawable_mut().draw(g);
    });
  }

  pub fn click(&mut self, x: i32, y: i32) -> bool {
    if x >= self.x && y >= self.y && x < self.x + self.width as i32 && y < self.y - self.height as i32 {
      let i = self.x;
      let i1 = self.y;
      self.drawable_mut().click(x - i, y - i1)
    } else {
      false
    }
  }

  pub fn into_inner(self) -> T {
    self.drawable
  }


@@ 68,8 45,15 @@ impl<T: Element> DerefMut for Container<T> {
}

impl<T: Element> Element for Container<T> {
  fn update(&mut self) {
    unimplemented!()
  }

  fn draw(&mut self, g: &mut Graphics) {
    self.draw(g)
    g.push_matrix(|g| {
      g.translatei(self.x, self.y, -1);
      self.drawable_mut().draw(g);
    });
  }

  fn set_size(&mut self, width: u32, height: u32) {


@@ 78,26 62,38 @@ impl<T: Element> Element for Container<T> {
    self.drawable_mut().set_size(width, height);
  }

  fn on_mouse_move(&mut self, mouse_x: i32, mouse_y: i32) {
    self.drawable_mut().on_mouse_move(mouse_x, mouse_y)
  }

  fn on_click(&mut self, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool {
    self.drawable_mut().on_click(mouse_x, mouse_y, state, button, modifiers)
  }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
    self.drawable.get_resize_capabilities(g)
  }

  fn is_animated(&self) -> bool {
    self.drawable().is_animated()
  }
}

impl<T: Element> Positionable for Container<T> {
  fn x(&self) -> i32 {
    self.x()
    self.x
  }

  fn y(&self) -> i32 {
    self.y()
    self.y
  }

  fn width(&self) -> u32 {
    self.width()
    self.width
  }

  fn height(&self) -> u32 {
    self.height()
    self.height
  }

  fn set_position(&mut self, x: i32, y: i32) {

M src/util/color.rs => src/util/color.rs +1 -1
@@ 64,6 64,6 @@ impl Color {
  pub fn alphaf(&self) -> f32 { self.a as f32 / 255.0 }

  pub fn into_array(self) -> [f32; 4] {
    [self.redf(), self.bluef(), self.greenf(), self.alphaf()]
    [self.redf(), self.greenf(), self.bluef(), self.alphaf()]
  }
}
\ No newline at end of file