~saiko/game

cbf7967398bfdd3e98e0f78b93092347fca88e19 — 2xsaiko 1 year, 17 days ago 40f16f4
Redesign panel/layout manager
M data/shader/tri.fsh => data/shader/tri.fsh +2 -2
@@ 1,10 1,10 @@
#version 400 core

in vec3 normal1;
in vec3 f_normal;

out vec4 color;

void main() {
    float light_str = dot(normalize(vec3(0.3, 0.25, 10)), normal1) * 0.8 + 0.2;
    float light_str = dot(normalize(vec3(0.3, 0.25, 10)), f_normal) * 0.8 + 0.2;
    color = vec4(vec3(0.3, 0.4, 0.7) * light_str, 1);
}
\ No newline at end of file

M data/shader/tri.vsh => data/shader/tri.vsh +2 -2
@@ 5,9 5,9 @@ uniform mat4 mvp;
in vec3 pos;
in vec3 normal;

out vec3 normal1;
out vec3 f_normal;

void main() {
    normal1 = normal;
    f_normal = normal;
    gl_Position = mvp * vec4(pos, 1);
}
\ No newline at end of file

A data/shader/uivp.fsh => data/shader/uivp.fsh +11 -0
@@ 0,0 1,11 @@
#version 400 core

uniform sampler2D texture;

in vec2 f_uv;

out vec4 color;

void main() {
    color = texture2D(texture, f_uv);
}
\ No newline at end of file

A data/shader/uivp.sdef => data/shader/uivp.sdef +2 -0
@@ 0,0 1,2 @@
vertex_shader   uivp.vsh
fragment_shader uivp.fsh
\ No newline at end of file

A data/shader/uivp.vsh => data/shader/uivp.vsh +13 -0
@@ 0,0 1,13 @@
#version 400 core

uniform mat4 mvp;

in ivec2 xy;
in vec2 uv;

out vec2 f_uv;

void main() {
    f_uv = uv;
    gl_Position = mvp * vec4(xy, 0, 1);
}
\ No newline at end of file

M src/main.rs => src/main.rs +60 -42
@@ 5,6 5,7 @@
#[macro_use]
extern crate glium;

use std::cell::RefCell;
use std::fs::{create_dir_all, File};
use std::io::Read;
use std::ops::Sub;


@@ 16,7 17,7 @@ use std::thread::sleep;
use std::time::{Duration, Instant};

use float_pretty_print::PrettyPrintFloat;
use glium::{BackfaceCullingMode, DepthTest, Display, DrawError, DrawParameters, Frame, Program, Surface, VertexBuffer};
use glium::{BackfaceCullingMode, DepthTest, Display, DrawError, DrawParameters, Program, Surface, VertexBuffer};
use glium::glutin::{ContextBuilder, GlProfile, GlRequest};
use glium::glutin::Api::OpenGl;
use glium::glutin::dpi::{LogicalPosition, LogicalSize};


@@ 41,11 42,11 @@ use crate::kb::Keyboard;
use crate::math::*;
use crate::res::{DataSource, ResourceLoader};
use crate::shader::ShaderManager;
use crate::ui::Element;
use crate::ui::element::Button;
use crate::ui::element::{Button, Element, Viewport};
use crate::ui::graphics::{Alignment, Graphics, StringDrawProps};
use crate::ui::layout::{BorderLayout, Position};
use crate::ui::layout::{BorderLayout, LayoutManager, StackLayout};
use crate::ui::panel::Panel;
use crate::ui::UiController;
use crate::util::{AnySurface, Color, LogPipe};

mod cam;


@@ 176,11 177,11 @@ fn start_game(env: Rc<Environment>) {
    }
  };

  let model = get_test_model(&mut sm, &display);
  let model = Rc::new(get_test_model(&mut sm, &display));

  let mut camera = Camera::new();
  let mut camera = Rc::new(RefCell::new(Camera::new()));
  let mut kb = Keyboard::new();
  camera.set_pos(vec3(0.0, 10.0, 0.0));
  camera.borrow_mut().set_pos(vec3(0.0, 10.0, 0.0));

  let mut current_fps = 0;
  let mut smooth_fps = 0;


@@ 196,50 197,66 @@ fn start_game(env: Rc<Environment>) {

  let fr = load_fonts(&env.rl, &display, &mut sm);

  let mut ui = Panel::new(&BorderLayout);
  ui.add(box Button::new("Click Me! T", || ()), Position::Top);
  ui.add(box Button::new("Click Me! B", || ()), Position::Bottom);
  ui.add(box Button::new("Click Me! L", || ()), Position::Left);
  ui.add(box Button::new("Click Me! R", || ()), Position::Right);
  ui.add(box Button::new("Click Me! C", || ()), Position::Center);
  struct TestUi<T: LayoutManager> {
    panel: Panel<T>,
  }

  impl<T: LayoutManager> UiController for TestUi<T> {
    fn get_element(&mut self) -> &mut dyn Element {
      unimplemented!()
    }
  }

  let mut ui = Panel::new(BorderLayout::new(
    Button::new("Click Me! T", || ()),
    Button::new("Click Me! B", || ()),
    Button::new("Click Me! L", || ()),
    Button::new("Click Me! R", || ()),
    Viewport::new(camera.clone(), model.clone()),
  ));

  let mut ui2 = Panel::new(StackLayout::new(vec![
    box Viewport::new(camera.clone(), model.clone()),
  ]));

  ui.resize(window.width(), window.height());
  ui2.resize(window.width(), window.height());

  while running.load(Ordering::Relaxed) {
    let current_ui: &mut dyn Element = if gui_mode { &mut ui } else { &mut ui2 };
    let time_start = Instant::now();
    let mut frame = display.draw();
    frame.clear(None, Some((0.0, 0.0, 0.0, 0.0)), false, Some(1.0), None);
    let mut props = RenderProperties::new(&camera, 90.0, window.width(), window.height());
    let mut props = RenderProperties::new(&camera.borrow(), 90.0, window.width(), window.height());

    let gui_mat = Mat4::get_ortho(0.0, window.width() as f32, window.height() as f32, 0.0, -10.0, 10.0);

    if !gui_mode {
      draw(&mut frame, &model, &props);
    }
    let gui_mat = Mat4::get_ortho(0.0, window.width() as f32, window.height() as f32, 0.0, -1000.0, 1000.0);

    let mut surface = AnySurface::Frame(&mut frame);
    let mut g = Graphics::new(&display, &mut surface, fr.choose((12, false, window.dpi)), &mut sm, gui_mat);

    if gui_mode {
      ui.draw(&mut g);
    }
    current_ui.draw(&mut g);

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

    let s = "Press [tab] to switch views";
    let color = Color::white();
    g.draw_string(s, window.width() as i32 - 2, 2, StringDrawProps::default().with_alignment(Alignment::Right));
    g.draw_string(&format!("fps: {} ({} actual)", smooth_fps, current_fps), 2, 2, StringDrawProps::default());
    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());
    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)", smooth_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();



@@ 254,7 271,7 @@ fn start_game(env: Rc<Environment>) {
        }
        Event::WindowEvent { event: WindowEvent::Resized(LogicalSize { width, height }), .. } => {
          window.on_resize(width, height);
          ui.resize(window.width(), window.height());
          current_ui.resize(window.width(), window.height());
        }
        Event::WindowEvent { event: WindowEvent::KeyboardInput { input, .. }, .. } => {
          if input.scancode == 15 && input.state == ElementState::Pressed {


@@ 278,13 295,14 @@ fn start_game(env: Rc<Environment>) {
        }
        Event::WindowEvent { event: WindowEvent::HiDpiFactorChanged(f), .. } => {
          window.on_dpi_changed(f);
          ui.resize(window.width(), window.height());
          current_ui.resize(window.width(), window.height());
        }
        _ => ()
      };
    });

    if !gui_mode {
      let mut camera = camera.borrow_mut();
      if let Some((mdiff_x, mdiff_y)) = mdiff {
        camera.rotate(mdiff_x as f32 * 0.1, -mdiff_y as f32 * 0.1, 0.0);
        gl_window.set_cursor_position(center).unwrap();


@@ 324,10 342,10 @@ fn start_game(env: Rc<Environment>) {
  }
}

fn draw(frame: &mut Frame, model: &Model<Vertex>, props: &RenderProperties) {
  frame.clear_color_and_depth((0.01, 0.01, 0.01, 1.0), 1.0);
fn draw(surface: &mut AnySurface, model: &Model<Vertex>, props: &RenderProperties) {
  surface.clear_color_and_depth((0.01, 0.01, 0.01, 1.0), 1.0);

  model.draw(frame, props)
  model.draw(surface, props)
    .expect("Failed to draw triangle!");
}



@@ 404,7 422,7 @@ fn load_fonts<'a>(rl: &ResourceLoader, facade: &'a Display, sm: &mut ShaderManag
implement_vertex!(Vertex, pos, normal);

#[derive(Copy, Clone)]
struct Vertex {
pub struct Vertex {
  pos: [f32; 3],
  normal: [f32; 3],
}


@@ 430,7 448,7 @@ impl Window {
  pub fn height(&self) -> u32 { (self.scaled_height * self.dpi) as u32 }
}

struct Model<T: Copy> {
pub struct Model<T: Copy> {
  shader: Rc<Program>,
  buf: VertexBuffer<T>,
}


@@ 456,7 474,7 @@ impl<T: Copy> Model<T> {
  }
}

struct RenderProperties {
pub struct RenderProperties {
  p: Mat4,
  mv: Mat4,
  mvp: Mat4,

M src/ui/element/button.rs => src/ui/element/button.rs +1 -1
@@ 1,4 1,4 @@
use crate::ui::Element;
use crate::ui::element::Element;
use crate::ui::graphics::{Alignment, Graphics, StringDrawProps};
use crate::ui::rescap::ResizeCapabilities;
use crate::util::Color;

M src/ui/element/mod.rs => src/ui/element/mod.rs +26 -1
@@ 1,6 1,31 @@
pub use button::*;
pub use spacer::*;
pub use viewport::*;

use crate::ui::graphics::Graphics;
use crate::ui::rescap::ResizeCapabilities;

mod button;
mod spacer;
mod viewport;
\ No newline at end of file
mod viewport;

pub trait Element {
  fn draw(&mut self, g: &mut Graphics);

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

  /// Resize this drawable. Behavior is undefined if dimensions are out of range.
  fn resize(&mut self, width: u32, height: u32);

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

impl Element for () {
  fn draw(&mut self, g: &mut Graphics) {}

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

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

M src/ui/element/spacer.rs => src/ui/element/spacer.rs +1 -1
@@ 1,4 1,4 @@
use crate::ui::Element;
use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::rescap::ResizeCapabilities;


M src/ui/element/viewport.rs => src/ui/element/viewport.rs +88 -7
@@ 1,22 1,103 @@
use std::cell::RefCell;
use std::pin::Pin;
use std::rc::Rc;

use glium::backend::Facade;
use glium::framebuffer::{DepthRenderBuffer, SimpleFrameBuffer};
use glium::Surface;
use glium::texture::{DepthFormat, RawImage2d, Texture2d};

use crate::{Model, RenderProperties, Vertex};
use crate::cam::Camera;
use crate::ui::Element;
use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::rescap::ResizeCapabilities;

pub struct Viewport {
  camera: Camera,
pub struct Viewport<'a> {
  camera: Rc<RefCell<Camera>>,
  model: Rc<Model<Vertex>>,
  width: u32,
  height: u32,
  fb: Option<ViewportFb<'a>>,
}

impl Viewport<'_> {
  pub fn new(camera: Rc<RefCell<Camera>>, model: Rc<Model<Vertex>>) -> Self {
    Viewport {
      camera,
      model,
      width: 0,
      height: 0,
      fb: None,
    }
  }
}

impl Element for Viewport {
impl Element for Viewport<'_> {
  fn draw(&mut self, g: &mut Graphics) {
    unimplemented!()
    let fb = match self.fb {
      Some(ref mut s) => s,
      None => {
        self.fb = Some(ViewportFb::new(g.facade(), self.width, self.height));
        self.fb.as_mut().unwrap()
      }
    };

    let props = RenderProperties::new(&*self.camera.borrow(), 90.0, self.width, self.height);

    fb.fb.clear_color_and_depth((0.01, 0.01, 0.01, 1.0), 1.0);
    self.model.draw(&mut fb.fb, &props).unwrap();

    g.draw_texture(0, 0, self.width as i32, self.height as i32, None, &fb.inner.texture);
  }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
    unimplemented!()
    ResizeCapabilities {
      min_width: Some(1),
      min_height: Some(1),
      preferred_width: None,
      preferred_height: None,
      max_width: None,
      max_height: None,
    }
  }

  fn resize(&mut self, width: u32, height: u32) {
    unimplemented!()
    self.width = width;
    self.height = height;
    self.fb = None;
  }
}

pub struct ViewportData {
  pub camera: Camera,
  pub model: Model<Vertex>,
}

struct ViewportFb<'a> {
  inner: Pin<Box<Inner>>,
  fb: SimpleFrameBuffer<'a>,
}

struct Inner {
  texture: Texture2d,
  depth: DepthRenderBuffer,
}

impl ViewportFb<'_> {
  pub fn new(facade: &dyn Facade, width: u32, height: u32) -> Self {
    let ri = RawImage2d::from_raw_rgba(vec![0.0; (width * height * 4) as usize], (width, height));
    let texture = Texture2d::new(facade, ri).unwrap();
    let depth = DepthRenderBuffer::new(facade, DepthFormat::F32, width, height).unwrap();
    let inner = Box::pin(Inner { texture, depth });
    let fb = unsafe {
      let texture = &*(&inner.texture as *const Texture2d);
      let depth = &*(&inner.depth as *const DepthRenderBuffer);
      SimpleFrameBuffer::with_depth_buffer(facade, texture, depth).unwrap()
    };
    ViewportFb {
      inner,
      fb,
    }
  }
}
\ No newline at end of file

M src/ui/graphics.rs => src/ui/graphics.rs +37 -1
@@ 1,4 1,4 @@
use glium::{DepthTest, DrawParameters, Surface, VertexBuffer};
use glium::{DepthTest, DrawParameters, Surface, Texture2d, VertexBuffer};
use glium::backend::Facade;
use glium::index::{NoIndices, PrimitiveType};
use glium::uniforms::UniformsStorage;


@@ 35,6 35,8 @@ impl<'a, 'b, 'c> Graphics<'a, 'b, 'c> {
    }
  }

  pub fn facade(&self) -> &dyn Facade { self.facade }

  pub fn surface(&self) -> &AnySurface<'b> { self.surface }

  pub fn surface_mut(&mut self) -> &mut AnySurface<'b> { self.surface }


@@ 97,6 99,40 @@ impl<'a, 'b, 'c> Graphics<'a, 'b, 'c> {
    ], color);
  }

  pub fn draw_texture(&mut self, x: i32, y: i32, width: i32, height: i32, dims: Option<(f32, f32, f32, f32)>, texture: &Texture2d) {
    #[derive(Copy, Clone)]
    struct Vertex {
      xy: [i32; 2],
      uv: [f32; 2],
    }

    implement_vertex!(Vertex, xy, uv);

    let (u, v, u1, v1) = dims.unwrap_or((0.0, 0.0, 1.0, 1.0));

    let x1 = x + width;
    let y1 = y + height;

    let vb = VertexBuffer::new(self.facade, &[
      Vertex { xy: [x, y], uv: [u, v1] },
      Vertex { xy: [x1, y], uv: [u1, v1] },
      Vertex { xy: [x, y1], uv: [u, v] },
      Vertex { xy: [x1, y1], uv: [u1, v] },
    ]).unwrap();

    let mut dp = DrawParameters::default();
    dp.depth.test = DepthTest::IfLessOrEqual;
    dp.depth.write = true;

    self.surface.draw(
      &vb,
      NoIndices(PrimitiveType::TriangleStrip),
      &*self.sm.get("shader/uivp.sdef").unwrap(),
      &UniformsStorage::new("mvp", *self.mvp().as_ref()).add("texture", texture),
      &dp,
    );
  }

  pub fn push_matrix<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
    self.mats.push(*self.modelview());
    let result = f(self);

M src/ui/layout/border.rs => src/ui/layout/border.rs +56 -60
@@ 1,9 1,11 @@
use std::cmp::max;

use crate::ui::layout::{LayoutElement, LayoutManager};
use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::layout::LayoutManager;
use crate::ui::layout::util::layout_stack;
use crate::ui::panel::PanelElement;
use crate::ui::rescap::ResizeCapabilities;
use crate::util::single::Single;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Position {


@@ 28,22 30,40 @@ pub enum Position {
/// |                 Bottom                 |
/// +----------------------------------------+
/// ```
pub struct BorderLayout;
pub struct BorderLayout<T, B, L, R, C>
  where T: Element, B: Element, L: Element, R: Element, C: Element
{
  top: PanelElement<T>,
  bottom: PanelElement<B>,
  left: PanelElement<L>,
  right: PanelElement<R>,
  center: PanelElement<C>,
}

impl LayoutManager for BorderLayout {
  type Data = Position;
impl<T, B, L, R, C> BorderLayout<T, B, L, R, C>
  where T: Element, B: Element, L: Element, R: Element, C: Element
{
  pub fn new(top: T, bottom: B, left: L, right: R, center: C) -> Self {
    BorderLayout {
      top: PanelElement::new(top),
      bottom: PanelElement::new(bottom),
      left: PanelElement::new(left),
      center: PanelElement::new(center),
      right: PanelElement::new(right),
    }
  }
}

  fn relayout(&self, elements: &mut [LayoutElement<Self::Data>], width: u32, height: u32) {
    let joined = |p| elements.iter()
      .filter(|el| *el.layout_data == p)
      .map(|el| el.caps())
      .single().or_none().unwrap_or(ResizeCapabilities::zero_size());
impl<T, B, L, R, C> LayoutManager for BorderLayout<T, B, L, R, C>
  where T: Element, B: Element, L: Element, R: Element, C: Element
{
  fn relayout(&mut self, g: &Graphics, width: u32, height: u32) {
    let top_caps = self.top.drawable().get_resize_capabilities(g);
    let bottom_caps = self.bottom.drawable().get_resize_capabilities(g);
    let left_caps = self.left.drawable().get_resize_capabilities(g);
    let right_caps = self.right.drawable().get_resize_capabilities(g);
    let center_caps = self.center.drawable().get_resize_capabilities(g);

    let top_caps = joined(Position::Top);
    let bottom_caps = joined(Position::Bottom);
    let left_caps = joined(Position::Left);
    let right_caps = joined(Position::Right);
    let center_caps = joined(Position::Center);
    let lcr_caps = left_caps.stack_right(center_caps).stack_right(right_caps);
    let full_caps = top_caps.stack_down(lcr_caps).stack_down(bottom_caps);



@@ 62,54 82,30 @@ impl LayoutManager for BorderLayout {
    let center_height = sizes[1];
    let bottom_height = sizes[2];

    for el in elements {
      match el.layout_data {
        Position::Top => {
          el.x = 0;
          el.y = 0;
          el.width = width;
          el.height = top_height;
        }
        Position::Bottom => {
          el.x = 0;
          el.y = (top_height + center_height) as i32;
          el.width = width;
          el.height = bottom_height;
        }
        Position::Left => {
          el.x = 0;
          el.y = top_height as i32;
          el.width = left_width;
          el.height = center_height;
        }
        Position::Right => {
          el.x = (left_width + center_width) as i32;
          el.y = top_height as i32;
          el.width = right_width;
          el.height = center_height;
        }
        Position::Center => {
          el.x = left_width as i32;
          el.y = top_height as i32;
          el.width = center_width;
          el.height = center_height;
        }
      }
    }
    self.top.set_dimensions(0, 0, width, top_height);
    self.bottom.set_dimensions(0, (top_height + center_height) as i32, width, bottom_height);
    self.left.set_dimensions(0, top_height as i32, left_width, center_height);
    self.right.set_dimensions((left_width + center_width) as i32, top_height as i32, right_width, center_height);
    self.center.set_dimensions(left_width as i32, top_height as i32, center_width, center_height);
  }

  fn get_resize_capabilities(&self, elements: &[LayoutElement<Self::Data>]) -> ResizeCapabilities {
    let joined = |p| elements.iter()
      .filter(|el| *el.layout_data == p)
      .map(|el| el.caps())
      .single().or_none().unwrap_or(ResizeCapabilities::zero_size());

    let top_caps = joined(Position::Top);
    let bottom_caps = joined(Position::Bottom);
    let left_caps = joined(Position::Left);
    let right_caps = joined(Position::Right);
    let center_caps = joined(Position::Center);
  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
    let top_caps = self.top.drawable().get_resize_capabilities(g);
    let bottom_caps = self.bottom.drawable().get_resize_capabilities(g);
    let left_caps = self.left.drawable().get_resize_capabilities(g);
    let right_caps = self.right.drawable().get_resize_capabilities(g);
    let center_caps = self.center.drawable().get_resize_capabilities(g);

    top_caps.stack_down(left_caps.stack_right(center_caps).stack_right(right_caps)).stack_down(bottom_caps)
  }

  fn get_drawables(&mut self) -> Vec<PanelElement<&mut dyn Element>> {
    vec![
      self.top.as_any_element(),
      self.bottom.as_any_element(),
      self.left.as_any_element(),
      self.right.as_any_element(),
      self.center.as_any_element(),
    ]
  }
}
\ No newline at end of file

M src/ui/layout/grid.rs => src/ui/layout/grid.rs +49 -49
@@ 1,49 1,49 @@
use crate::ui::layout::{LayoutElement, LayoutManager};
use crate::ui::rescap::ResizeCapabilities;

pub struct GridLayout {
  h_spacing: u32,
  v_spacing: u32,
}

impl GridLayout {
  pub fn new(h_spacing: u32, v_spacing: u32) -> Self {
    GridLayout { h_spacing, v_spacing }
  }

  fn get_table_sizes(&self, elements: &[LayoutElement<<Self as LayoutManager>::Data>], width: u32, height: u32) -> (Vec<u32>, Vec<u32>) {
    match elements {
      &[] => (vec![], vec![]),
      &[_, ..] => {
        let max_column = elements.iter().map(|el| el.layout_data.0).max().unwrap();
        let max_row = elements.iter().map(|el| el.layout_data.1).max().unwrap();
        let mut columns = vec![vec![]; max_column + 1];
        let mut rows = vec![vec![]; max_row + 1];

        for el in elements.iter() {
          let &(col, row) = el.layout_data;
          columns[col].push(&el.caps);
          rows[row].push(&el.caps);
        }

        unimplemented!()
      }
    }
  }
}

impl LayoutManager for GridLayout {
  type Data = (usize, usize);

  fn relayout(&self, elements: &mut [LayoutElement<Self::Data>], width: u32, height: u32) {
    unimplemented!()
  }

  fn get_resize_capabilities(&self, elements: &[LayoutElement<Self::Data>]) -> ResizeCapabilities {
    unimplemented!()
  }
}

fn compute_divs(data: &[(usize, u32, u32)]) -> Vec<u32> {
  unimplemented!()
}
\ No newline at end of file
//use crate::ui::layout::{LayoutElement, LayoutManager};
//use crate::ui::rescap::ResizeCapabilities;
//
//pub struct GridLayout {
//  h_spacing: u32,
//  v_spacing: u32,
//}
//
//impl GridLayout {
//  pub fn new(h_spacing: u32, v_spacing: u32) -> Self {
//    GridLayout { h_spacing, v_spacing }
//  }
//
//  fn get_table_sizes(&self, elements: &[LayoutElement<<Self as LayoutManager>::Data>], width: u32, height: u32) -> (Vec<u32>, Vec<u32>) {
//    match elements {
//      &[] => (vec![], vec![]),
//      &[_, ..] => {
//        let max_column = elements.iter().map(|el| el.layout_data.0).max().unwrap();
//        let max_row = elements.iter().map(|el| el.layout_data.1).max().unwrap();
//        let mut columns = vec![vec![]; max_column + 1];
//        let mut rows = vec![vec![]; max_row + 1];
//
//        for el in elements.iter() {
//          let &(col, row) = el.layout_data;
//          columns[col].push(&el.caps);
//          rows[row].push(&el.caps);
//        }
//
//        unimplemented!()
//      }
//    }
//  }
//}
//
//impl LayoutManager for GridLayout {
//  type Data = (usize, usize);
//
//  fn relayout(&self, elements: &mut [LayoutElement<Self::Data>], width: u32, height: u32) {
//    unimplemented!()
//  }
//
//  fn get_resize_capabilities(&self, elements: &[LayoutElement<Self::Data>]) -> ResizeCapabilities {
//    unimplemented!()
//  }
//}
//
//fn compute_divs(data: &[(usize, u32, u32)]) -> Vec<u32> {
//  unimplemented!()
//}
\ No newline at end of file

M src/ui/layout/mod.rs => src/ui/layout/mod.rs +6 -36
@@ 3,6 3,9 @@ pub use grid::*;
pub use noop::*;
pub use stack::*;

use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::panel::PanelElement;
use crate::ui::rescap::ResizeCapabilities;

mod border;


@@ 11,43 14,10 @@ mod noop;
mod stack;
mod util;

pub struct LayoutElement<'a, T> {
  caps: ResizeCapabilities,
  x: i32,
  y: i32,
  width: u32,
  height: u32,
  layout_data: &'a T,
}

impl<'a, T> LayoutElement<'a, T> {
  pub fn new(caps: ResizeCapabilities, x: i32, y: i32, width: u32, height: u32, layout_data: &'a T) -> Self {
    LayoutElement { caps, x, y, width, height, layout_data }
  }

  pub fn caps(&self) -> ResizeCapabilities { self.caps }

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

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

  pub fn data(&self) -> &T { self.layout_data }

  pub fn set_pos(&mut self, x: i32, y: i32) {
    self.x = x;
    self.y = y;
  }

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

pub trait LayoutManager {
  type Data;
  fn relayout(&mut self, g: &Graphics, width: u32, height: u32);

  fn relayout(&self, elements: &mut [LayoutElement<Self::Data>], width: u32, height: u32);
  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities;

  fn get_resize_capabilities(&self, elements: &[LayoutElement<Self::Data>]) -> ResizeCapabilities;
  fn get_drawables(&mut self) -> Vec<PanelElement<&mut dyn Element>>;
}
\ No newline at end of file

M src/ui/layout/noop.rs => src/ui/layout/noop.rs +21 -7
@@ 1,14 1,28 @@
use crate::ui::layout::{LayoutElement, LayoutManager};
use crate::ui::ResizeCapabilities;
use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::layout::LayoutManager;
use crate::ui::panel::PanelElement;
use crate::ui::rescap::ResizeCapabilities;

pub struct NoopLayout;
pub struct NoopLayout {
  elements: Vec<PanelElement<Box<dyn Element>>>,
}

impl NoopLayout {
  pub fn new(elements: Vec<Box<dyn Element>>) -> Self {
    NoopLayout {
      elements: elements.into_iter().map(PanelElement::new).collect()
    }
  }
}

impl LayoutManager for NoopLayout {
  type Data = ();
  fn relayout(&mut self, g: &Graphics, width: u32, height: u32) {}

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

  fn relayout(&self, elements: &mut [LayoutElement<Self::Data>], width: u32, height: u32) {}

  fn get_resize_capabilities(&self, elements: &[LayoutElement<Self::Data>]) -> ResizeCapabilities {
    ResizeCapabilities::default()
  fn get_drawables(&mut self) -> Vec<PanelElement<&mut dyn Element>> {
    self.elements.iter_mut().map(PanelElement::as_any_element).collect()
  }
}
\ No newline at end of file

M src/ui/layout/stack.rs => src/ui/layout/stack.rs +25 -13
@@ 1,22 1,34 @@
use crate::ui::layout::{LayoutElement, LayoutManager};
use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::layout::LayoutManager;
use crate::ui::panel::PanelElement;
use crate::ui::rescap::ResizeCapabilities;

pub struct StackLayout;
pub struct StackLayout {
  elements: Vec<PanelElement<Box<dyn Element>>>,
}

impl LayoutManager for StackLayout {
  type Data = ();
impl StackLayout {
  pub fn new(elements: Vec<Box<dyn Element>>) -> Self {
    StackLayout {
      elements: elements.into_iter().map(PanelElement::new).collect()
    }
  }
}

  fn relayout(&self, elements: &mut [LayoutElement<Self::Data>], width: u32, height: u32) {
    for el in elements {
      el.width = width;
      el.height = height;
      el.x = 0;
      el.y = 0;
impl LayoutManager for StackLayout {
  fn relayout(&mut self, g: &Graphics, width: u32, height: u32) {
    for el in self.elements.iter_mut() {
      el.set_dimensions(0, 0, width, height);
    }
  }

  fn get_resize_capabilities(&self, elements: &[LayoutElement<Self::Data>]) -> ResizeCapabilities {
    elements.iter()
      .fold(ResizeCapabilities::default(), |acc, a| acc.combine(a.caps()))
  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
    self.elements.iter()
      .fold(ResizeCapabilities::default(), |acc, a| acc.combine(a.drawable().get_resize_capabilities(g)))
  }

  fn get_drawables(&mut self) -> Vec<PanelElement<&mut dyn Element>> {
    self.elements.iter_mut().map(PanelElement::as_any_element).collect()
  }
}
\ No newline at end of file

M src/ui/mod.rs => src/ui/mod.rs +54 -7
@@ 1,5 1,5 @@
use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::rescap::ResizeCapabilities;

pub mod console;
pub mod layout;


@@ 8,13 8,60 @@ pub mod panel;
pub mod rescap;
pub mod graphics;

pub trait Element {
  fn draw(&mut self, g: &mut Graphics);
pub trait UiController {
  fn get_element(&mut self) -> &mut dyn Element;

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities;
  fn resize(&mut self, width: u32, height: u32) {
    self.get_element().resize(width, height);
  }

  /// Resize this drawable. Behavior is undefined if dimensions are out of range.
  fn resize(&mut self, width: u32, height: u32);
  fn open(&mut self) {}

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

pub struct UiHost {
  current_controller: Option<Box<dyn UiController>>,
  width: u32,
  height: u32,
}

impl UiHost {
  pub fn new() -> Self {
    UiHost {
      current_controller: None,
      width: 0,
      height: 0,
    }
  }

  pub fn close_ui(&mut self) -> Option<Box<dyn UiController>> {
    let mut r = self.current_controller.take();
    if let Some(ref mut r) = r {
      r.close();
    }
    r
  }

  pub fn open_ui(&mut self, mut element: impl UiController + 'static) -> Option<Box<dyn UiController>> {
    let r = self.close_ui();
    element.open();
    element.resize(self.width, self.height);
    self.current_controller = Some(box element);
    r
  }

  pub fn resize(&mut self, width: u32, height: u32) {
    self.width = width;
    self.height = height;
    if let Some(ref mut c) = self.current_controller {
      c.resize(width, height);
    }
  }

  pub fn draw(&mut self, g: &mut Graphics) {
    if let Some(ref mut c) = self.current_controller {
      c.get_element().draw(g)
    }
  }
}
\ No newline at end of file

M src/ui/panel.rs => src/ui/panel.rs +87 -55
@@ 1,73 1,101 @@
use crate::ui::Element;
use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::layout::{LayoutElement, LayoutManager};
use crate::ui::layout::LayoutManager;
use crate::ui::rescap::ResizeCapabilities;

pub struct Panel<'a, T> {
  elements: Vec<PanelElement<T>>,
  layout_manager: &'a dyn LayoutManager<Data=T>,
pub trait AsMutElement {
  fn as_element(&self) -> &dyn Element;

  fn as_mut_element(&mut self) -> &mut dyn Element;
}

impl<T> AsMutElement for T where T: Element {
  fn as_element(&self) -> &dyn Element { self }

  fn as_mut_element(&mut self) -> &mut dyn Element { self }
}

impl AsMutElement for &mut dyn Element {
  fn as_element(&self) -> &dyn Element { *self }

  fn as_mut_element(&mut self) -> &mut dyn Element { *self }
}

impl AsMutElement for Box<dyn Element> {
  fn as_element(&self) -> &dyn Element { self.as_ref() }

  fn as_mut_element(&mut self) -> &mut dyn Element { self.as_mut() }
}

pub struct Panel<T: LayoutManager> {
  layout_manager: T,
  width: u32,
  height: u32,
  needs_relayout: bool,
}

pub struct PanelElement<T> {
  drawable: Box<dyn Element>,
pub struct PanelElement<T: AsMutElement> {
  drawable: T,
  x: i32,
  y: i32,
  width: u32,
  height: u32,
  layout_data: T,
}

impl<'a, T> Panel<'a, T> {
  pub fn new(lm: &'a dyn LayoutManager<Data=T>) -> Self {
    Panel {
      elements: vec![],
      layout_manager: lm,
impl<T: AsMutElement> PanelElement<T> {
  pub fn new(el: T) -> Self {
    PanelElement {
      drawable: el,
      x: 0,
      y: 0,
      width: 0,
      height: 0,
      needs_relayout: false,
    }
  }

  pub fn add(&mut self, drawable: Box<dyn Element>, layout_data: T) {
    let el = PanelElement {
      drawable,
      x: 0,
      y: 0,
      width: 0,
      height: 0,
      layout_data,
    };
    self.elements.push(el);
    self.schedule_relayout();
  pub fn drawable(&self) -> &T { &self.drawable }

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

  pub fn set_dimensions(&mut self, x: i32, y: i32, width: u32, height: u32) {
    self.x = x;
    self.y = y;
    self.width = width;
    self.height = height;
    self.drawable.as_mut_element().resize(width, height);
  }

  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 as_any_element(&mut self) -> PanelElement<&mut dyn Element> {
    PanelElement {
      drawable: self.drawable.as_mut_element(),
      x: self.x,
      y: self.y,
      width: self.width,
      height: self.height,
    }
  }
}

  fn get_layout_elements(&self, g: &Graphics) -> Vec<LayoutElement<T>> {
    self.elements.iter()
      .map(|el| LayoutElement::new(
        el.drawable.get_resize_capabilities(g),
        el.x,
        el.y,
        el.width,
        el.height,
        &el.layout_data,
      )).collect()
impl<T: LayoutManager> Panel<T> {
  pub fn new(lm: T) -> Self {
    Panel {
      layout_manager: lm,
      width: 0,
      height: 0,
      needs_relayout: false,
    }
  }

  pub fn relayout(&mut self, g: &Graphics) {
    let mut data = self.get_layout_elements(g);
    self.layout_manager.relayout(&mut data, self.width, self.height);
    let data = data.into_iter().map(|el| (el.pos(), el.size())).collect::<Vec<_>>();
    self.elements.iter_mut().zip(data.into_iter())
      .for_each(|(a, ((x, y), (width, height)))| {
        a.x = x;
        a.y = y;
        a.width = width;
        a.height = height;
        a.drawable.resize(width, height);
      });
    self.layout_manager.relayout(g, self.width, self.height);
  }

  pub fn schedule_relayout(&mut self) {


@@ 75,23 103,23 @@ impl<'a, T> Panel<'a, T> {
  }
}

impl<'a, T> Element for Panel<'a, T> {
impl<T: LayoutManager> Element for Panel<T> {
  fn draw(&mut self, g: &mut Graphics) {
    if self.needs_relayout {
      self.relayout(g);
      self.needs_relayout = false;
    }

    for el in self.elements.iter_mut() {
    for mut el in self.layout_manager.get_drawables() {
      g.push_matrix(|g| {
        g.translatei(el.x, el.y, 1);
        el.drawable.draw(g);
        g.translatei(el.x(), el.y(), 1);
        el.drawable_mut().draw(g);
      });
    }
  }

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

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


@@ 101,9 129,13 @@ impl<'a, T> Element for Panel<'a, T> {
  }

  fn click(&mut self, mouse_x: i32, mouse_y: i32) -> bool {
    self.elements.iter_mut()
      .filter(|&&mut PanelElement { x, y, width, height, .. }|
        mouse_x >= x && mouse_x < (x + width as i32) && mouse_y >= y && mouse_y < (y + height as i32))
      .fold(false, |acc, a| acc || a.drawable.click(mouse_x - a.x, mouse_y - a.y))
    self.layout_manager.get_drawables().iter_mut()
      .filter(|el|
        mouse_x >= el.x() && mouse_x < (el.x() + el.width() as i32) && mouse_y >= el.y() && mouse_y < (el.y() + el.height() as i32))
      .fold(false, |acc, a| {
        let x = a.x();
        let y = a.y();
        acc || a.drawable_mut().click(mouse_x - x, mouse_y - y)
      })
  }
}
\ No newline at end of file