~saiko/game

247a0c368e9bb5cf19074153027cc83dbbdc93ee — 2xsaiko 1 year, 9 days ago 974b80a
Mouse select
M src/main.rs => src/main.rs +10 -6
@@ 45,8 45,8 @@ 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::testui::TestUiController;
use crate::ui::graphics::{DrawPreview, Graphics, StringDrawProps};
use crate::ui::host::UiHost;
use crate::util::{AnySurface, Color, LogPipe};



@@ 238,6 238,8 @@ pub struct Game<'a> {
  fr: Multifont<(i32, bool, f64), (i32, bool), BakedFont<'a>, SelImpl>,
}

const FONT_SIZE: i32 = 32;

impl Game<'_> {
  fn handle_event(&mut self, event: Event<()>, target: &EventLoopWindowTarget<()>, cf: &mut ControlFlow) {
    match event {


@@ 259,7 261,7 @@ impl Game<'_> {
      Event::WindowEvent { event: WindowEvent::KeyboardInput { input, .. }, .. } => {
        self.ui_host.on_key(input.state, input.virtual_keycode, input.scancode, input.modifiers);
      }
      Event::WindowEvent{event:WindowEvent::ReceivedCharacter(c), ..} => {
      Event::WindowEvent { event: WindowEvent::ReceivedCharacter(c), .. } => {
        self.ui_host.on_key_typed(c);
      }
      Event::WindowEvent { event: WindowEvent::Focused(state), .. } => {


@@ 271,10 273,12 @@ 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);
        self.ui_host.on_mouse_move(&DrawPreview::from(self.fr.choose((FONT_SIZE, false, self.window.dpi))),
                                   (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)
        self.ui_host.on_click(&DrawPreview::from(self.fr.choose((FONT_SIZE, false, self.window.dpi))),
                              state, button, modifiers)
      }
      Event::WindowEvent { event: WindowEvent::HiDpiFactorChanged(f), .. } => {
        self.window.on_dpi_changed(f);


@@ 342,7 346,7 @@ impl Game<'_> {
    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, self.fr.choose((12, false, window.dpi)), &mut self.sm, gui_mat);
    let mut g = Graphics::new(display, &mut surface, self.fr.choose((FONT_SIZE, false, window.dpi)), &mut self.sm, gui_mat);

    self.ui_host.draw(&mut g);


M src/testui.rs => src/testui.rs +3 -3
@@ 4,8 4,8 @@ use std::rc::Rc;
use crate::{Model, Vertex};
use crate::cam::Camera;
use crate::ui::controller::UiController;
use crate::ui::element::{Button, ButtonEvent, Viewport, InputField};
use crate::ui::graphics::{Graphics, StringDrawProps};
use crate::ui::element::{Button, InputField, Viewport};
use crate::ui::graphics::{DrawPreview, Graphics, StringDrawProps};
use crate::ui::layout::{BorderLayout, LayoutManager};
use crate::ui::panel::Container;
use crate::ui::rescap::ResizeCapabilities;


@@ 94,7 94,7 @@ impl UiController for TestUiController {
    self.get_layout().relayout(elements, g, width, height);
  }

  fn get_resize_capabilities(&self, elements: &Self::Elements, g: &Graphics) -> ResizeCapabilities {
  fn get_resize_capabilities(&self, elements: &Self::Elements, g: &DrawPreview) -> ResizeCapabilities {
    self.get_layout().get_resize_capabilities(elements, g)
  }


M src/ui/controller.rs => src/ui/controller.rs +2 -2
@@ 1,5 1,5 @@
use crate::ui::access::UiAccess;
use crate::ui::graphics::Graphics;
use crate::ui::graphics::{DrawPreview, Graphics};
use crate::ui::rescap::ResizeCapabilities;

pub trait UiController {


@@ 11,7 11,7 @@ pub trait UiController {

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

  fn get_resize_capabilities(&self, elements: &Self::Elements, g: &Graphics) -> ResizeCapabilities;
  fn get_resize_capabilities(&self, elements: &Self::Elements, g: &DrawPreview) -> ResizeCapabilities;

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

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

use crate::ui::element::{ACTIVE_COLOR, Element, HIGHLIGHTED_COLOR, INACTIVE_COLOR, NORMAL_COLOR, FocusResult, default_try_focus};
use crate::ui::element::{ACTIVE_COLOR, default_try_focus, Element, FocusResult, HIGHLIGHTED_COLOR, INACTIVE_COLOR, NORMAL_COLOR};
use crate::ui::event::EventQueue;
use crate::ui::graphics::{Alignment, Graphics, StringDrawProps};
use crate::ui::graphics::{Alignment, DrawPreview, Graphics, StringDrawProps};
use crate::ui::panel::Container;
use crate::ui::rescap::ResizeCapabilities;
use crate::util::Color;
use crate::ui::util::is_in_bounds;
use crate::util::Color;

pub enum ButtonEvent {
  Pressed,


@@ 70,11 70,11 @@ impl Element for Button {
    self.height = height;
  }

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

  fn on_click(&mut self, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool {
  fn on_click(&mut self, g: &DrawPreview, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool {
    let is_in_bounds = is_in_bounds(0, 0, self.width as i32, self.height as i32, mouse_x, mouse_y);
    if !self.is_enabled {
      is_in_bounds


@@ 96,7 96,7 @@ impl Element for Button {
    default_try_focus(self.width as i32, self.height as i32, self.is_enabled, &mut false, mouse_x, mouse_y)
  }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
  fn get_resize_capabilities(&self, g: &DrawPreview) -> ResizeCapabilities {
    let fr = g.font_renderer();
    ResizeCapabilities::new(
      Some(6 + fr.get_str_width(&self.text) as u32),

M src/ui/element/inputfield.rs => src/ui/element/inputfield.rs +86 -14
@@ 2,17 2,21 @@ use std::borrow::Cow;
use std::cmp::{max, min};

use clipboard::{ClipboardContext, ClipboardProvider};
use glium::glutin::event::{ElementState, ModifiersState, ScanCode, VirtualKeyCode};
use glium::glutin::event::{ElementState, ModifiersState, MouseButton, ScanCode, VirtualKeyCode};
use unicode_segmentation::UnicodeSegmentation;

use crate::ui::element::{default_try_focus, Element, FocusResult, HIGHLIGHTED_COLOR, INACTIVE_COLOR, NORMAL_COLOR};
use crate::ui::event::EventQueue;
use crate::ui::graphics::{Graphics, StringDrawProps};
use crate::ui::graphics::{DrawPreview, Graphics, StringDrawProps};
use crate::ui::panel::Container;
use crate::ui::rescap::ResizeCapabilities;
use crate::ui::util::is_in_bounds;
use crate::util::Color;

pub enum InputFieldEvent {}
pub enum InputFieldEvent {
  KeyInput(char),
  EnterPressed,
}

pub struct InputField {
  overlay_text: String,


@@ 23,6 27,8 @@ pub struct InputField {
  height: u32,
  is_enabled: bool,
  is_focused: bool,
  is_mouse_selecting: bool,
  select_words: bool,
  queue: EventQueue<InputFieldEvent>,
}



@@ 37,6 43,8 @@ impl InputField {
      height: 0,
      is_enabled: true,
      is_focused: false,
      is_mouse_selecting: false,
      select_words: false,
      queue: EventQueue::new(),
    })
  }


@@ 139,6 147,34 @@ impl InputField {
  fn has_selected(&self) -> bool {
    self.cursor_position != self.selection_start
  }

  fn get_cursor_pos_for_px(&self, g: &DrawPreview, x: i32, y: i32, words: bool) -> usize {
    if y < 0 {
      0
    } else if y > self.height as i32 {
      self.text.len()
    } else {
      let mut len = 3;
      if words {
        for (i, word) in self.text.split_word_bound_indices() {
          let x1 = g.font_renderer().get_str_width(word) as i32;
          if len + x1 / 2 > x {
            return i;
          }
          len += x1;
        }
      } else {
        for (i, gr) in self.text.grapheme_indices(true) {
          let x1 = g.font_renderer().get_str_width(gr) as i32;
          if len + x1 / 2 > x {
            return i;
          }
          len += x1;
        }
      }
      self.text.len()
    }
  }
}

impl Element for InputField {


@@ 156,8 192,9 @@ impl Element for InputField {
        let x = 3 + g.font_renderer().get_str_width(&self.text[..min]) as i32;
        let width = g.font_renderer().get_str_width(&self.text[min..max]) as i32;

        g.translatef(0.0, 0.0, -0.01);
        g.draw_rect(x, 3, width, g.font_renderer().get_height() as i32, Color::from_rgb8(0xfc, 0xff, 0x26));
        g.translatef(0.0, 0.0, -0.1);
        // g.draw_rect(x, 3, width, g.font_renderer().get_height() as i32, Color::from_rgb8(0x1a, 0xff, 0x17));
        g.fill_rect(x, 3, width, 1 + g.font_renderer().get_height() as i32, Color::from_rgb8(0x11, 0x11, 0x18));
      });
    }



@@ 166,14 203,15 @@ impl Element for InputField {
                  StringDrawProps::default().with_color(Color::white()));

    if self.is_focused {
      let cur_at = max(6, 3 + g.font_renderer().get_str_width(&self.text[..self.cursor_position]) as i32);
      let width = g.font_renderer().get_height() as i32 / 5;
      let cur_at = 3 + max(width, g.font_renderer().get_str_width(&self.text[..self.cursor_position]) as i32);
      let c = Color::white();
      let end = 3 + g.font_renderer().get_height() as i32;
      g.draw_line(cur_at, 3, cur_at, end - 1, c);
      g.draw_line(cur_at - 4, 3, cur_at - 1, 3, c);
      g.draw_line(cur_at, 3, cur_at + 3, 3, c);
      g.draw_line(cur_at - 4, end, cur_at - 1, end, c);
      g.draw_line(cur_at, end, cur_at + 3, end, c);
      g.draw_line(cur_at, 4, cur_at, end - 1, c);
      g.draw_line(cur_at - width, 3, cur_at - 1, 3, c);
      g.draw_line(cur_at + 1, 3, cur_at + width, 3, c);
      g.draw_line(cur_at - width, end, cur_at - 1, end, c);
      g.draw_line(cur_at + 1, end, cur_at + width, end, c);
    }
  }



@@ 182,9 220,40 @@ impl Element for InputField {
    self.height = height;
  }

  fn on_mouse_move(&mut self, g: &DrawPreview, mouse_x: i32, mouse_y: i32) {
    if self.is_mouse_selecting {
      self.cursor_position = self.get_cursor_pos_for_px(g, mouse_x, mouse_y, self.select_words);
    }
  }

  fn on_click(&mut self, g: &DrawPreview, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool {
    if button == MouseButton::Left && state == ElementState::Released {
      self.is_mouse_selecting = false;
      self.select_words = false;
    }
    if is_in_bounds(0, 0, self.width as i32, self.height as i32, mouse_x, mouse_y) {
      if self.can_type() && button == MouseButton::Left && state == ElementState::Pressed {
        self.cursor_position = self.get_cursor_pos_for_px(g, mouse_x, mouse_y, false);
        if !modifiers.shift { self.selection_start = self.cursor_position; }
        self.is_mouse_selecting = true;
      }
      true
    } else {
      false
    }
  }

  fn on_key(&mut self, state: ElementState, vk: Option<VirtualKeyCode>, scancode: ScanCode, modifiers: ModifiersState) {
    let mut m_ctrl = ModifiersState::default();
    m_ctrl.ctrl = true;
    if self.is_mouse_selecting {
      match vk {
        Some(VirtualKeyCode::LControl) | Some(VirtualKeyCode::RControl) => {
          self.select_words = state == ElementState::Pressed;
        }
        _ => {}
      }
    }
    if self.can_type() && state == ElementState::Pressed {
      match vk {
        Some(VirtualKeyCode::Back) => {


@@ 259,8 328,11 @@ impl Element for InputField {
          let inserted_text = ClipboardProvider::new().ok()
            .and_then(|mut clip: ClipboardContext| clip.get_contents().ok())
            .map_or(Cow::Borrowed(""), |s| Cow::Owned(s));
          self.text.insert_str(self.cursor_position, &*inserted_text);
          self.cursor_position += inserted_text.len();
          if !inserted_text.is_empty() {
            self.text.insert_str(self.cursor_position, &*inserted_text);
            self.cursor_position += inserted_text.len();
            self.selection_start = self.cursor_position;
          }
        }
        _ => {}
      }


@@ 284,7 356,7 @@ impl Element for InputField {
    self.is_focused = false;
  }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
  fn get_resize_capabilities(&self, g: &DrawPreview) -> ResizeCapabilities {
    ResizeCapabilities::new(None, Some(g.font_renderer().get_height() as u32 + 6), None, None, None, None) // TODO
  }
}
\ No newline at end of file

M src/ui/element/mod.rs => src/ui/element/mod.rs +4 -4
@@ 5,7 5,7 @@ pub use inputfield::*;
pub use spacer::*;
pub use viewport::*;

use crate::ui::graphics::Graphics;
use crate::ui::graphics::{DrawPreview, Graphics};
use crate::ui::rescap::ResizeCapabilities;
use crate::ui::util::is_in_bounds;
use crate::util::Color;


@@ 27,9 27,9 @@ pub trait Element {

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

  fn on_mouse_move(&mut self, mouse_x: i32, mouse_y: i32) {}
  fn on_mouse_move(&mut self, g: &DrawPreview, 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 on_click(&mut self, g: &DrawPreview, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool { true }

  fn on_key(&mut self, state: ElementState, vk: Option<VirtualKeyCode>, scancode: ScanCode, modifiers: ModifiersState) {}



@@ 39,7 39,7 @@ pub trait Element {

  fn on_focus_lost(&mut self) {}

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

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

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

pub struct Spacer;


@@ 9,7 9,7 @@ impl Element for Spacer {

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

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

M src/ui/element/viewport.rs => src/ui/element/viewport.rs +2 -2
@@ 10,7 10,7 @@ use glium::texture::{DepthFormat, RawImage2d, Texture2d};
use crate::{Model, RenderProperties, Vertex};
use crate::cam::Camera;
use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::graphics::{DrawPreview, Graphics};
use crate::ui::panel::Container;
use crate::ui::rescap::ResizeCapabilities;



@@ 58,7 58,7 @@ impl Element for Viewport {
    self.fb = None;
  }

  fn get_resize_capabilities(&self, g: &Graphics) -> ResizeCapabilities {
  fn get_resize_capabilities(&self, g: &DrawPreview) -> ResizeCapabilities {
    ResizeCapabilities {
      min_width: Some(1),
      min_height: Some(1),

M src/ui/graphics.rs => src/ui/graphics.rs +55 -14
@@ 20,7 20,7 @@ pub struct Graphics<'a, 'b, 'c> {

  // for batched rendering
  buffered_strings: Vec<(String, f32, f32, Color, Transformation)>,
  buffered_lines: Vec<(i32, i32, i32, i32, Color, Transformation)>,
  buffered_tris: Vec<(i32, i32, Color, Transformation)>,
}

impl<'a, 'b, 'c> Graphics<'a, 'b, 'c> {


@@ 33,7 33,7 @@ impl<'a, 'b, 'c> Graphics<'a, 'b, 'c> {
      perspective,
      modelview: vec![Transformation::Identity],
      buffered_strings: vec![],
      buffered_lines: vec![],
      buffered_tris: vec![],
    }
  }



@@ 43,7 43,7 @@ impl<'a, 'b, 'c> Graphics<'a, 'b, 'c> {

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

  pub fn font_renderer(&self) -> &dyn FontRenderer { self.fr }
  pub fn font_renderer(&self) -> &'a dyn FontRenderer { self.fr }

  pub fn perspective(&self) -> &Mat4 { &self.perspective }



@@ 80,8 80,39 @@ impl<'a, 'b, 'c> Graphics<'a, 'b, 'c> {
    self.buffered_strings.push((t.to_owned(), x as f32, y as f32, props.color, *self.modelview()));
  }

  pub fn fill_tris(&mut self, tris: &[[[i32; 2]; 3]], color: Color) {
    tris.iter().flatten().for_each(|[x, y]| self.buffered_tris.push((*x, *y, color, *self.modelview())));
  }

  pub fn fill_rect(&mut self, x: i32, y: i32, width: i32, height: i32, color: Color) {
    self.fill_tris(&[
      [[x, y], [x + width, y], [x, y + height]],
      [[x + width, y], [x, y + height], [x + width, y + height]],
    ], color);
  }

  pub fn draw_line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Color) {
    self.buffered_lines.push((x1, y1, x2, y2, color, *self.modelview()));
    let line_width = 1;
    let horizontal = (x1 - x2).abs() > (y1 - y2).abs();
    if horizontal {
      let yoff1 = -line_width / 2;
      let yoff2 = (line_width + 1) / 2;
      let xoff1 = if x1 > x2 { 1 } else { 0 };
      let xoff2 = if x1 > x2 { 0 } else { 1 };
      self.fill_tris(&[
        [[x1 + xoff1, y1 + yoff1], [x1 + xoff1, y1 + yoff2], [x2 + xoff2, y2 + yoff1]],
        [[x1 + xoff1, y1 + yoff2], [x2 + xoff2, y2 + yoff1], [x2 + xoff2, y2 + yoff2]],
      ], color);
    } else {
      let xoff1 = -line_width / 2;
      let xoff2 = (line_width + 1) / 2;
      let yoff1 = if y1 > y2 { 1 } else { 0 };
      let yoff2 = if y1 > y2 { 0 } else { 1 };
      self.fill_tris(&[
        [[x1 + xoff1, y1 + yoff1], [x1 + xoff2, y1 + yoff1], [x2 + xoff1, y2 + yoff2]],
        [[x1 + xoff2, y1 + yoff1], [x2 + xoff1, y2 + yoff2], [x2 + xoff2, y2 + yoff2]],
      ], color);
    }
  }

  pub fn draw_polygon(&mut self, coords: &[[i32; 2]], color: Color) {


@@ 101,10 132,6 @@ impl<'a, 'b, 'c> Graphics<'a, 'b, 'c> {
    ], color);
  }

  pub fn draw_filled_rect(&mut self, x: i32, y: i32, width: i32, height: i32, color: Color) {
    todo!()
  }

  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 {


@@ 162,36 189,38 @@ impl<'a, 'b, 'c> Graphics<'a, 'b, 'c> {
    }

    {
      implement_vertex!(PolygonPoint, xyz, color);

      #[derive(Copy, Clone)]
      struct PolygonPoint {
        xyz: [f32; 3],
        color: [f32; 4],
      }

      implement_vertex!(PolygonPoint, xyz, color);

      let mut data = vec![];
      for (x, y, x1, y1, color, tr) in self.buffered_lines.drain(..) {
      for (x, y, color, tr) in self.buffered_tris.drain(..) {
        data.push(PolygonPoint { xyz: *tr.transform_vec3(vec3(x as f32, y as f32, 0.0)).as_ref(), color: color.into_array() });
        data.push(PolygonPoint { xyz: *tr.transform_vec3(vec3(x1 as f32, y1 as f32, 0.0)).as_ref(), color: color.into_array() });
      };

      let vb = VertexBuffer::new(self.facade, &data).unwrap();
      let mut dp = DrawParameters::default();
      dp.line_width = Some(1.0);
      dp.depth.test = DepthTest::IfLessOrEqual;
      dp.depth.write = true;

      let perspective = *self.perspective.as_ref();
      self.surface.draw(
        &vb,
        NoIndices(PrimitiveType::LinesList),
        NoIndices(PrimitiveType::TrianglesList),
        &self.sm.get("shader/uipoly.sdef").unwrap(),
        &UniformsStorage::new("mvp", perspective),
        &dp,
      ).unwrap();
    }
  }

  pub fn as_draw_preview(&self) -> DrawPreview<'a> {
    DrawPreview::from(self.fr)
  }
}

impl Drop for Graphics<'_, '_, '_> {


@@ 200,6 229,18 @@ impl Drop for Graphics<'_, '_, '_> {
  }
}

pub struct DrawPreview<'a> {
  fr: &'a BakedFont<'a>,
}

impl<'a> DrawPreview<'a> {
  pub fn from(fr: &'a BakedFont<'a>) -> Self {
    DrawPreview { fr }
  }

  pub fn font_renderer(&self) -> &dyn FontRenderer { self.fr }
}

#[derive(Clone, Copy, Debug)]
pub struct StringDrawProps {
  pub alignment: Alignment,

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

use crate::ui::element::Element;
use crate::ui::graphics::Graphics;
use crate::ui::graphics::{DrawPreview, Graphics};

pub struct UiHost {
  current_controller: Option<Box<dyn Element>>,


@@ 61,18 61,18 @@ impl UiHost {
    }
  }

  pub fn on_mouse_move(&mut self, mouse_x: i32, mouse_y: i32) {
  pub fn on_mouse_move(&mut self, g: &DrawPreview, 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);
      c.on_mouse_move(g, mouse_x, mouse_y);
    }
  }

  pub fn on_click(&mut self, state: ElementState, button: MouseButton, modifiers: ModifiersState) {
  pub fn on_click(&mut self, g: &DrawPreview, state: ElementState, button: MouseButton, modifiers: ModifiersState) {
    if let Some(ref mut c) = self.current_controller {
      c.try_focus(self.mouse_x, self.mouse_y);
      c.on_click(self.mouse_x, self.mouse_y, state, button, modifiers);
      c.on_click(g, self.mouse_x, self.mouse_y, state, button, modifiers);
    }
  }


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

use crate::ui::graphics::Graphics;
use crate::ui::access::UiAccess;
use crate::ui::graphics::{DrawPreview, Graphics};
use crate::ui::layout::LayoutManager;
use crate::ui::layout::util::layout_stack;
use crate::ui::rescap::ResizeCapabilities;
use crate::ui::access::UiAccess;

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


@@ 60,11 60,12 @@ impl<X: UiAccess, T, B, L, R, C> LayoutManager<X> for BorderLayout<X, T, B, L, R
  where T: LayoutManager<X>, B: LayoutManager<X>, L: LayoutManager<X>, R: LayoutManager<X>, C: LayoutManager<X>
{
  fn relayout_at(&self, ui: &mut X, g: &Graphics, x: i32, y: i32, width: u32, height: u32) {
    let top_caps = self.top.get_resize_capabilities(ui, g);
    let bottom_caps = self.bottom.get_resize_capabilities(ui, g);
    let left_caps = self.left.get_resize_capabilities(ui, g);
    let right_caps = self.right.get_resize_capabilities(ui, g);
    let center_caps = self.center.get_resize_capabilities(ui, g);
    let dp = g.as_draw_preview();
    let top_caps = self.top.get_resize_capabilities(ui, &dp);
    let bottom_caps = self.bottom.get_resize_capabilities(ui, &dp);
    let left_caps = self.left.get_resize_capabilities(ui, &dp);
    let right_caps = self.right.get_resize_capabilities(ui, &dp);
    let center_caps = self.center.get_resize_capabilities(ui, &dp);

    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);


@@ 91,7 92,7 @@ impl<X: UiAccess, T, B, L, R, C> LayoutManager<X> for BorderLayout<X, T, B, L, R
    self.center.relayout_at(ui, g, x + left_width as i32, y + top_height as i32, center_width, center_height);
  }

  fn get_resize_capabilities(&self, ui: &X, g: &Graphics) -> ResizeCapabilities {
  fn get_resize_capabilities(&self, ui: &X, g: &DrawPreview) -> ResizeCapabilities {
    let top_caps = self.top.get_resize_capabilities(ui, g);
    let bottom_caps = self.bottom.get_resize_capabilities(ui, g);
    let left_caps = self.left.get_resize_capabilities(ui, g);

M src/ui/layout/mod.rs => src/ui/layout/mod.rs +4 -4
@@ 2,9 2,9 @@ pub use border::*;
pub use grid::*;
pub use stack::*;

use crate::ui::graphics::Graphics;
use crate::ui::rescap::ResizeCapabilities;
use crate::ui::access::UiAccess;
use crate::ui::graphics::{DrawPreview, Graphics};
use crate::ui::rescap::ResizeCapabilities;

mod border;
mod grid;


@@ 18,13 18,13 @@ pub trait LayoutManager<T: UiAccess> {

  fn relayout_at(&self, ui: &mut T, g: &Graphics, x: i32, y: i32, width: u32, height: u32);

  fn get_resize_capabilities(&self, ui: &T, g: &Graphics) -> ResizeCapabilities;
  fn get_resize_capabilities(&self, ui: &T, g: &DrawPreview) -> ResizeCapabilities;
}

impl<T: UiAccess> LayoutManager<T> for () {
  fn relayout_at(&self, ui: &mut T, g: &Graphics, x: i32, y: i32, width: u32, height: u32) {}

  fn get_resize_capabilities(&self, ui: &T, g: &Graphics) -> ResizeCapabilities {
  fn get_resize_capabilities(&self, ui: &T, g: &DrawPreview) -> ResizeCapabilities {
    ResizeCapabilities::zero_size()
  }
}
\ No newline at end of file

M src/ui/mod.rs => src/ui/mod.rs +7 -7
@@ 3,7 3,7 @@ use glium::glutin::event::{ElementState, ModifiersState, MouseButton, ScanCode, 
use crate::ui::access::{UiAccess, Values};
use crate::ui::controller::UiController;
use crate::ui::element::{Element, FocusResult, Positionable};
use crate::ui::graphics::Graphics;
use crate::ui::graphics::{DrawPreview, Graphics};
use crate::ui::rescap::ResizeCapabilities;

pub mod access;


@@ 58,15 58,15 @@ impl<E: UiAccess + 'static, C: UiController<Elements=E>> Element for GenUi<E, C>
    self.is_dirty = true;
  }

  fn on_mouse_move(&mut self, mouse_x: i32, mouse_y: i32) {
  fn on_mouse_move(&mut self, g: &DrawPreview, mouse_x: i32, mouse_y: i32) {
    self.for_each(|e| {
      e.on_mouse_move(mouse_x - e.x(), mouse_y - e.y());
      e.on_mouse_move(g, 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 {
  fn on_click(&mut self, g: &DrawPreview, 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);
      e.on_click(g, mouse_x - e.x(), mouse_y - e.y(), state, button, modifiers);
    });
    true
  }


@@ 111,7 111,7 @@ impl<E: UiAccess + 'static, C: UiController<Elements=E>> Element for GenUi<E, C>
    self.for_each(|e| e.on_focus_lost());
  }

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



@@ 158,7 158,7 @@ macro_rules! _ui_def_inner {
        $crate::ui::access::UiAccess::get_element_mut(ui, *self).set_dimensions(x, y, width, height);
      }

      fn get_resize_capabilities(&self, ui: &$el_struct, g: &$crate::ui::graphics::Graphics) -> $crate::ui::rescap::ResizeCapabilities {
      fn get_resize_capabilities(&self, ui: &$el_struct, g: &$crate::ui::graphics::DrawPreview) -> $crate::ui::rescap::ResizeCapabilities {
        $crate::ui::access::UiAccess::get_element(ui, *self).get_resize_capabilities(g)
      }
    }

M src/ui/panel.rs => src/ui/panel.rs +7 -7
@@ 3,7 3,7 @@ use std::ops::{Deref, DerefMut};
use glium::glutin::event::{ElementState, ModifiersState, MouseButton, ScanCode, VirtualKeyCode};

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

pub struct Container<T: Element> {


@@ 51,7 51,7 @@ impl<T: Element> Element for Container<T> {

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


@@ 62,12 62,12 @@ 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_mouse_move(&mut self, g: &DrawPreview, mouse_x: i32, mouse_y: i32) {
    self.drawable_mut().on_mouse_move(g, 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 on_click(&mut self, g: &DrawPreview, mouse_x: i32, mouse_y: i32, state: ElementState, button: MouseButton, modifiers: ModifiersState) -> bool {
    self.drawable_mut().on_click(g, mouse_x, mouse_y, state, button, modifiers)
  }

  fn on_key(&mut self, state: ElementState, vk: Option<VirtualKeyCode>, scancode: ScanCode, modifiers: ModifiersState) {


@@ 86,7 86,7 @@ impl<T: Element> Element for Container<T> {
    self.drawable_mut().on_focus_lost()
  }

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