├── .gitignore ├── Cargo.toml ├── README.md ├── examples └── basic.rs └── src ├── color.rs ├── lib.rs ├── renderer.rs ├── shaders ├── circle.frag ├── polygon.frag └── polygon.vert ├── ui.rs ├── util.rs └── widgets ├── button.rs ├── knob.rs └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | .idea 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dd-gui" 3 | version = "0.1.0" 4 | authors = ["Rob Saunders "] 5 | 6 | [dependencies] 7 | glium = "0.17.0" 8 | glutin = "0.9.1" 9 | winit = "0.7.5" 10 | cgmath = "0.14.1" 11 | 12 | [lib] 13 | name = "dd_gui" 14 | 15 | [[example]] 16 | name = "basic" 17 | 18 | #[replace] 19 | #"winit:0.7.0" = { path = "/Users/rob/Projects/winit" } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dd-gui 2 | Minimal, hardware accelerated immediate mode OpenGL GUI library in rust. 3 | 4 | The first goal of this library will be to support VST development, but there's no reason it can't be used for other purposes. It directly uses a glium surface, so can draw on top of glium based games, etc. 5 | 6 | Short term roadmap: 7 | - [x] renderer: rect 8 | - [x] renderer: circle 9 | - [ ] renderer: arc 10 | - [ ] renderer: text 11 | - [ ] renderer: image 12 | - [x] ui: drag and drop (added as a PR to winit core) 13 | - [x] ui: mouse state (move, click, drag) 14 | - [ ] styles: defaults 15 | - [ ] styles: borders for all shapes 16 | - [ ] widgets: knob 17 | - [x] widgets: button 18 | - [ ] widgets: slider 19 | -------------------------------------------------------------------------------- /examples/basic.rs: -------------------------------------------------------------------------------- 1 | extern crate dd_gui; 2 | 3 | use dd_gui::glutin; 4 | use dd_gui::glium; 5 | 6 | use dd_gui::{ Point, Rect }; 7 | use dd_gui::widgets::{ Button, Knob }; 8 | use dd_gui::color; 9 | 10 | fn main() { 11 | let mut events_loop = glutin::EventsLoop::new(); 12 | let window = glutin::WindowBuilder::new().with_dimensions(640,480); 13 | let context = glutin::ContextBuilder::new(); 14 | let display = glium::Display::new(window, context, &events_loop).unwrap(); 15 | 16 | let mut renderer = dd_gui::Renderer::new(display); 17 | let mut ui = dd_gui::Ui::new(&mut renderer); 18 | 19 | let mut last_update = std::time::Instant::now(); 20 | 'main: loop { 21 | let now = std::time::Instant::now(); 22 | let duration_since_last_update = now.duration_since(last_update); 23 | 24 | let sixteen_ms = std::time::Duration::from_millis(16); 25 | 26 | if duration_since_last_update < sixteen_ms { 27 | std::thread::sleep(sixteen_ms - duration_since_last_update) 28 | } else { 29 | // Display FPS: 30 | // println!("FPS: {}", 1_000_000_000 / duration_since_last_update.subsec_nanos()); 31 | 32 | let mut events = Vec::new(); 33 | events_loop.poll_events(|e| events.push(e)); 34 | 35 | ui.handle_events(&events); 36 | 37 | Button::new(Rect{ origin: Point::new(100., 100.), size: Point::new(10., 100.) }) 38 | .color(color::GREEN) 39 | .handle_events(&events, &mut ui, "green button".to_string()) 40 | .draw(&mut renderer); 41 | 42 | Button::new(Rect{ origin: Point::new(130., 100.), size: Point::new(10., 100.) }) 43 | .color(color::YELLOW) 44 | .handle_events(&events, &mut ui, "yellow button".to_string()) 45 | .draw(&mut renderer); 46 | 47 | Button::new(Rect{ origin: Point::new(10., 10.), size: Point::new(50., 40.) }) 48 | .draw(&mut renderer); 49 | 50 | if Knob::new(Rect{ origin: Point::new(20.,20.), size: Point::new(80.,50.) }) 51 | .color(color::rgba(255, 200, 100, 150)) 52 | .handle(&events, &mut ui, "default knob".to_string()) 53 | .draw(&mut renderer) 54 | .changed() { println!("default knob!") }; 55 | 56 | // if Knob::new(Rect{ origin: Point::new(150.,190.), size: Point::new(400.,400.) }) 57 | // .color(color::PINK) 58 | // .handle(&events, &mut ui, "pink circle".to_string()) 59 | // .draw(&mut renderer) 60 | // .changed() { println!("pink circle!"); }; 61 | 62 | renderer.render(); 63 | 64 | use dd_gui::winit; 65 | use dd_gui::winit::{ Event, WindowEvent }; 66 | 67 | // break the loop on esc or window close. 68 | for event in events { 69 | match event { 70 | Event::WindowEvent { event, .. } => { 71 | match event { 72 | WindowEvent::Closed => { break 'main }, 73 | WindowEvent::KeyboardInput { 74 | input: winit::KeyboardInput { virtual_keycode: Some(winit::VirtualKeyCode::Escape), .. }, .. 75 | } => { break 'main }, 76 | _ => () 77 | } 78 | }, 79 | _ => () 80 | } 81 | } 82 | 83 | last_update = now; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/color.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 2 | pub struct Color { 3 | r: u8, 4 | g: u8, 5 | b: u8, 6 | a: u8, 7 | } 8 | 9 | pub fn rgba(r:u8, g:u8, b:u8, a: u8) -> Color { 10 | Color { r:r, g:g, b:b, a:a } 11 | } 12 | 13 | pub fn rgb(r:u8, g:u8, b:u8) -> Color { 14 | Color { r:r, g:g, b:b, a: 255} 15 | } 16 | 17 | pub const RED: Color = Color { r:240, g:0, b:0, a:255 }; 18 | pub const GREEN: Color = Color { r:0, g:255, b:0, a:255 }; 19 | pub const YELLOW: Color = Color { r:255, g:255, b:0, a:255 }; 20 | pub const BLUE: Color = Color { r:0, g:0, b:255, a:255 }; 21 | pub const WHITE: Color = Color { r:255, g:255, b:255, a:255 }; 22 | pub const BLACK: Color = Color { r:0, g:0, b:0, a:255 }; 23 | pub const PINK: Color = Color { r:255, g:105, b:180, a:255 }; 24 | 25 | impl Color { 26 | pub fn as_f32(&self) -> (f32, f32, f32, f32) { 27 | ( 28 | self.r as f32 / 255.0, 29 | self.g as f32 / 255.0, 30 | self.b as f32 / 255.0, 31 | self.a as f32 / 255.0 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_imports)] 2 | 3 | #[macro_use] pub extern crate glium; 4 | 5 | pub extern crate winit; 6 | pub extern crate glutin; 7 | 8 | pub extern crate cgmath; 9 | pub type Matrix2 = cgmath::Matrix2; 10 | pub type Matrix3 = cgmath::Matrix3; 11 | pub type Matrix4 = cgmath::Matrix4; 12 | 13 | mod renderer; 14 | pub use renderer::Renderer; 15 | 16 | pub mod widgets; 17 | pub mod color; 18 | pub mod ui; 19 | pub use ui::Ui; 20 | 21 | #[derive(Clone, Debug)] 22 | pub struct Rect { 23 | pub origin: Point, 24 | pub size: Point, 25 | } 26 | 27 | impl Rect { 28 | pub fn coords(&self) -> (Point, Point) { 29 | ( 30 | Point{ x:self.origin.x, y:self.origin.y }, 31 | Point{ x:(self.origin.x + self.size.w()), y:(self.origin.y + self.size.h()) } 32 | ) 33 | } 34 | } 35 | 36 | #[derive(Clone, Debug, Copy)] 37 | pub struct Point { 38 | pub x: f32, 39 | pub y: f32, 40 | } 41 | 42 | impl Point { 43 | pub fn new(x: f32, y: f32) -> Self { Self{ x:x, y:y }} 44 | pub fn xy(&self) -> (f32, f32) { (self.x, self.y) } 45 | pub fn wh(&self) -> (f32, f32) { (self.x, self.y) } 46 | pub fn w(&self) -> f32 { self.x } 47 | pub fn h(&self) -> f32 { self.y } 48 | } 49 | -------------------------------------------------------------------------------- /src/renderer.rs: -------------------------------------------------------------------------------- 1 | use cgmath; 2 | use glium; 3 | use glium::Surface; 4 | 5 | use Rect; 6 | use color::Color; 7 | 8 | #[derive(Clone)] 9 | pub enum RenderElement { 10 | Triangle(Rect, Color), 11 | Circle(Rect, Color), 12 | } 13 | 14 | pub struct Renderer { 15 | pub display: glium::Display, 16 | pub triangle_program: glium::Program, 17 | pub circle_program: glium::Program, 18 | pub instructions: Vec, 19 | } 20 | 21 | #[derive(Copy, Clone)] 22 | struct Vertex { 23 | position: [f32; 2], 24 | } 25 | 26 | implement_vertex!(Vertex, position); 27 | 28 | impl Renderer { 29 | pub fn new(display: glium::Display) -> Renderer { 30 | let triangle_program = program_from_shader(&display, 31 | include_str!("shaders/polygon.vert"), 32 | include_str!("shaders/polygon.frag")); 33 | 34 | let circle_program = program_from_shader(&display, 35 | include_str!("shaders/polygon.vert"), 36 | include_str!("shaders/circle.frag")); 37 | Renderer { 38 | display: display, 39 | instructions: Vec::new(), 40 | triangle_program: triangle_program, 41 | circle_program: circle_program, 42 | } 43 | } 44 | 45 | pub fn get_inner_size_points(&mut self) -> (f32, f32) { 46 | let points = self.display.gl_window().get_inner_size_points().unwrap(); 47 | (points.0 as f32, points.1 as f32) 48 | } 49 | 50 | pub fn render(&mut self) { 51 | let mut target = self.display.draw(); 52 | target.clear_color(0.005, 0.005, 0.005, 1.0); 53 | 54 | let (view_width, view_height) = self.get_inner_size_points(); 55 | 56 | let projection: [[f32; 4]; 4] = cgmath::ortho(0.0, view_width, 0.0, view_height, -1.0, 1.0).into(); 57 | 58 | let draw_params = glium::DrawParameters { 59 | blend: glium::Blend::alpha_blending(), 60 | .. Default::default() 61 | }; 62 | 63 | for instruction in self.instructions.clone() { 64 | match instruction { 65 | RenderElement::Circle(position, color) => { 66 | let (min, max) = position.coords(); 67 | 68 | let uniforms = uniform! { 69 | ortho_projection: projection, 70 | u_resolution: [view_width, view_height], 71 | u_color: color.as_f32(), 72 | u_position: [(min.x + max.x), (min.y + max.y)], 73 | u_radius: position.size.w() / 2., 74 | }; 75 | 76 | let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList); 77 | 78 | target.draw( 79 | &quad(&self.display, position), 80 | &indices, 81 | &self.circle_program, 82 | &uniforms, 83 | &draw_params).unwrap(); 84 | }, 85 | RenderElement::Triangle(position, color) => { 86 | 87 | let uniforms = uniform! { 88 | ortho_projection: projection, 89 | u_resolution: [view_width, view_height], 90 | u_color: color.as_f32(), 91 | }; 92 | 93 | let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList); 94 | 95 | target.draw( 96 | &quad(&self.display, position), 97 | &indices, 98 | &self.triangle_program, 99 | &uniforms, 100 | &draw_params).unwrap(); 101 | 102 | } 103 | } 104 | } 105 | target.finish().expect("target to unwrap"); 106 | self.instructions = Vec::new(); // clear the instruction queue. 107 | } 108 | } 109 | 110 | pub fn program_from_shader(display: &glium::Display, vertex_shader: &str, fragment_shader: &str) -> glium::Program { 111 | glium::Program::from_source( 112 | display, 113 | &vertex_shader, 114 | &fragment_shader, 115 | None).expect("program to complete.") 116 | } 117 | 118 | fn quad(display: &glium::Display, position: Rect) -> glium::VertexBuffer { 119 | let (min, max) = position.coords(); 120 | let shape = vec![ 121 | Vertex { position: [min.x, min.y] }, 122 | Vertex { position: [max.x, max.y] }, 123 | Vertex { position: [min.x, max.y] }, 124 | Vertex { position: [min.x, min.y] }, 125 | Vertex { position: [max.x, max.y] }, 126 | Vertex { position: [max.x, min.y] }, 127 | ]; 128 | glium::VertexBuffer::new(display, &shape).unwrap() 129 | } 130 | -------------------------------------------------------------------------------- /src/shaders/circle.frag: -------------------------------------------------------------------------------- 1 | #version 140 2 | 3 | uniform vec2 u_resolution; 4 | uniform vec4 u_color; 5 | uniform vec2 u_position; 6 | uniform vec2 u_midpoint; 7 | uniform float u_radius; 8 | 9 | //in vec4 v_color; 10 | out vec4 color; 11 | 12 | float smoothFloat(float r, float R) { 13 | return (1.0-smoothstep(R-1.0,R+1.0, r)); 14 | } 15 | 16 | float circleLine(vec2 uv, vec2 center, float radius, float width) { 17 | float r = length(uv - center); 18 | return smoothFloat(r-width/2.0,radius) - smoothFloat(r+width/2.0,radius); 19 | } 20 | 21 | float circleFill(vec2 uv, vec2 center, float radius) { 22 | float r = length(uv - center); 23 | return smoothFloat(r, radius); 24 | } 25 | 26 | void main() { 27 | vec3 finalColor = vec3(0.0); 28 | vec2 uv = gl_FragCoord.xy; 29 | float borderWidth = 3.; 30 | vec2 c = u_position.xy; 31 | // float padding = (borderWidth * 2.); // account for border so image doesn't chop off. 32 | 33 | // line 34 | vec3 lineColor = vec3(0.3, 0.9, 0.9); 35 | float lineAlpha = circleLine(uv, c, u_radius, borderWidth); 36 | finalColor += lineColor * lineAlpha; 37 | 38 | // fill 39 | vec4 fillColor = u_color; 40 | float fillAlpha = (1.0 - lineAlpha) * circleFill(uv, c, u_radius); 41 | finalColor += fillColor.rgb * fillAlpha; 42 | 43 | // discard transparent pixel draws 44 | if((fillAlpha + lineAlpha) == 0.) discard; 45 | 46 | // blend final color 47 | color = vec4(finalColor, (fillAlpha + lineAlpha)); 48 | } 49 | -------------------------------------------------------------------------------- /src/shaders/polygon.frag: -------------------------------------------------------------------------------- 1 | #version 140 2 | 3 | uniform vec2 u_resolution; 4 | uniform vec4 u_color; 5 | out vec4 color; 6 | 7 | void main() { 8 | // vec2 st = gl_FragCoord.xy / u_resolution; 9 | // color = vec4(st.x, st.y, 0.0, 1.0); 10 | color = u_color; 11 | } 12 | -------------------------------------------------------------------------------- /src/shaders/polygon.vert: -------------------------------------------------------------------------------- 1 | #version 140 2 | 3 | uniform mat4 ortho_projection; 4 | 5 | in vec2 position; 6 | 7 | void main() { 8 | vec4 position_matrix = vec4(0.1, 0.1, 0.0, 1.0); 9 | gl_Position = ortho_projection * vec4(position, 0.0, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/ui.rs: -------------------------------------------------------------------------------- 1 | use glutin; 2 | use Point; 3 | use Rect; 4 | use Renderer; 5 | 6 | /// Ui: 7 | /// Tracks the ui's global state across frames. As widgets do not keep state across frames we 8 | /// need a way of keeping track of multi-frame info eg. the mouses state, dragging, etc. 9 | #[derive(Clone, Debug)] 10 | pub struct Ui { 11 | size: (f32, f32), 12 | pub mouse: MouseState, 13 | pub focused_widget: Option, 14 | } 15 | 16 | // todo: add time ago for state change. for tweens and animations. 17 | #[derive(Clone, Debug)] 18 | pub struct FocusedWidget { 19 | id: String, 20 | state: FocusedWidgetState, 21 | } 22 | 23 | #[derive(Clone, Debug, PartialEq)] 24 | pub enum FocusedWidgetState { 25 | Hovered, 26 | Pressed, 27 | Activated, 28 | } 29 | 30 | /// WidgetState: 31 | /// Tracks the current state of the widget on a per-frame basis. 32 | #[derive(Clone, Copy, Debug)] 33 | pub struct WidgetState { 34 | pub hovered: bool, // about to interact, eg. mouse over, tab-order in 35 | pub hot: bool, // engaged eg. mouse is down, possibly dragging 36 | pub activated: bool, // actually interacted eg. mouse up while hovered, key down, etc 37 | } 38 | 39 | #[derive(Clone, Copy, Debug)] 40 | pub struct MouseState { 41 | pub position: Point, 42 | pub state: MouseButton, 43 | } 44 | 45 | #[derive(Clone, Copy, PartialEq, Debug)] 46 | pub enum MouseButton { 47 | Idle, 48 | Down, 49 | Up, 50 | } 51 | 52 | impl Ui { 53 | pub fn new(renderer: &mut Renderer) -> Self { 54 | let size = renderer.get_inner_size_points(); 55 | Self { 56 | mouse: MouseState { position: Point{ x:0., y:0. }, state: MouseButton::Idle }, 57 | size: size, 58 | focused_widget: None, 59 | } 60 | } 61 | 62 | pub fn is_mouse_down(&self) -> bool { 63 | self.mouse.state == MouseButton::Down 64 | } 65 | 66 | pub fn set_hovered(&mut self, id: String) { 67 | self.focused_widget = Some( 68 | FocusedWidget { 69 | id: id, 70 | state: FocusedWidgetState::Hovered, 71 | } 72 | ); 73 | } 74 | 75 | /// Updates widget state against ui 76 | pub fn update(&mut self, id: String, position: Rect) -> Option { 77 | let mouse_inside = self.mouse_inside_rect(position); 78 | let is_focus = self.is_focused(id.clone()); 79 | let mouse_is_down = self.is_mouse_down(); 80 | let ui_locked = self.is_locked(); 81 | let mouse_was_clicked = self.mouse.state == MouseButton::Up; 82 | 83 | if mouse_was_clicked && is_focus { 84 | // a mouseup occurred somewhere in the widget. 85 | self.clear_focus(); 86 | if mouse_inside { 87 | // widget has completed a click. 88 | return Some(FocusedWidgetState::Activated); 89 | } else { 90 | // user cancelled by mousing up outside the widget. 91 | return None; 92 | } 93 | } 94 | 95 | if mouse_inside && !ui_locked { 96 | if mouse_is_down { 97 | // mouse has been depressed on the widget. 98 | self.set_pressed(id.clone()); 99 | return Some(FocusedWidgetState::Pressed); 100 | } else { 101 | // mouse is hovering over widget. 102 | self.set_hovered(id.clone()); 103 | return Some(FocusedWidgetState::Hovered); 104 | } 105 | }; 106 | 107 | if is_focus && mouse_is_down { 108 | // mouse was depressed on the widget and has moved outside the widget. 109 | return Some(FocusedWidgetState::Pressed); 110 | } 111 | 112 | None 113 | } 114 | 115 | pub fn set_pressed(&mut self, id: String) { 116 | self.focused_widget = Some( 117 | FocusedWidget { 118 | id: id, 119 | state: FocusedWidgetState::Pressed, 120 | } 121 | ); 122 | } 123 | 124 | pub fn clear_focus(&mut self) { 125 | self.focused_widget = None; 126 | } 127 | 128 | pub fn is_focused(&self, id: String) -> bool { 129 | match self.focused_widget { 130 | Some(ref w) => { w.id == id } 131 | None => { false } 132 | } 133 | } 134 | 135 | // a widget has control and has locked the focus. can be forcibly broken. 136 | pub fn is_locked(&self) -> bool { 137 | match self.focused_widget { 138 | Some(ref w) => { w.state == FocusedWidgetState::Pressed } 139 | None => { false } 140 | } 141 | } 142 | 143 | pub fn handle_events(&mut self, events: &[glutin::Event]) { 144 | use glutin::WindowEvent::{ MouseInput, MouseMoved }; 145 | use glutin::{ ElementState }; 146 | 147 | // reset the mouse button on a mouseup from the previous frame, otherwise it will click forever. 148 | if self.mouse.state == MouseButton::Up { 149 | self.mouse.state = MouseButton::Idle; 150 | } 151 | 152 | use winit::{ Event, WindowEvent }; 153 | 154 | // updates mouse state 155 | for event in events { 156 | match event { 157 | &Event::WindowEvent { ref event, .. } => { 158 | match event { 159 | 160 | &MouseInput{ state: ElementState::Pressed, button: glutin::MouseButton::Left, .. } => { 161 | // clear the focus on a mouse down (if any widget misbehaves this should override). 162 | self.clear_focus(); 163 | self.mouse.state = MouseButton::Down; 164 | }, 165 | 166 | &MouseInput{ state: ElementState::Released, button: glutin::MouseButton::Left, .. } => { 167 | // clear the focus on a mouse down (if any widget misbehaves this should override). 168 | self.mouse.state = MouseButton::Up; 169 | }, 170 | 171 | &MouseMoved{ position: (x, y), .. } => { 172 | self.mouse.position = Point { x: x as f32, y: y as f32 }; 173 | } 174 | 175 | _ => () 176 | } 177 | }, 178 | _ => () 179 | } 180 | } 181 | } 182 | 183 | pub fn mouse_inside_rect(&mut self, rect: Rect) -> bool { 184 | let (x, y) = ( 185 | (self.mouse.position.x / 2.), 186 | (self.size.1 - (self.mouse.position.y / 2.))); 187 | let (min, max) = rect.coords(); 188 | 189 | (x > min.x && y > min.y && 190 | x < max.x && y < max.y) 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monomadic/dd-gui/a424ab50d4e50732cc211a1463259940be691042/src/util.rs -------------------------------------------------------------------------------- /src/widgets/button.rs: -------------------------------------------------------------------------------- 1 | use renderer::RenderElement; 2 | use Renderer; 3 | use Rect; 4 | use color; 5 | use color::Color; 6 | use Ui; 7 | use ui::{ MouseButton, WidgetState, FocusedWidget, FocusedWidgetState }; 8 | 9 | use glutin; 10 | 11 | pub struct Button { 12 | position: Rect, 13 | color: Color, 14 | state: ButtonState, 15 | } 16 | 17 | enum ButtonState { 18 | Idle, 19 | Hovered, 20 | Pressed, 21 | } 22 | 23 | impl Button { 24 | pub fn new(rect: Rect) -> Button { 25 | Button { 26 | position: rect, 27 | color: color::RED, 28 | state: ButtonState::Idle, 29 | } 30 | } 31 | 32 | pub fn color(mut self, color: Color) -> Self { self.color = color; self } 33 | 34 | pub fn draw(&self, renderer: &mut Renderer) { 35 | // let color = match self.state { 36 | // ButtonState::Hovered => { color::BLUE }, 37 | // ButtonState::Pressed => { color::RED }, 38 | // _ => { self.color } 39 | // }; 40 | 41 | renderer.instructions.push( 42 | RenderElement::Triangle(self.position.clone(), self.color) 43 | ); 44 | } 45 | 46 | pub fn handle_events(mut self, events: &[glutin::Event], ui: &mut Ui, id: String) -> Self { 47 | let mouse_inside = ui.mouse_inside_rect(self.position.clone()); 48 | let is_focus = ui.is_focused(id.clone()); 49 | 50 | // for event in events { 51 | // match event { 52 | // &glutin::Event::MouseInput(glutin::ElementState::Released, glutin::MouseButton::Left) => { 53 | // if mouse_inside && is_focus { 54 | // println!("clicked: {}", id); 55 | // } 56 | // }, 57 | // _ => () 58 | // } 59 | // } 60 | 61 | match ui.update(id.clone(), self.position.clone()) { 62 | Some(state) => { 63 | match state { 64 | FocusedWidgetState::Hovered => { self.color = color::BLUE; } 65 | FocusedWidgetState::Pressed => { self.color = color::RED; } 66 | FocusedWidgetState::Activated => { self.color = color::BLUE; println!("clicked: {}", id.clone()); } 67 | } 68 | }, 69 | None => () 70 | }; 71 | 72 | self 73 | } 74 | 75 | pub fn clicked(self) -> bool { 76 | false 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/widgets/knob.rs: -------------------------------------------------------------------------------- 1 | use renderer::RenderElement; 2 | 3 | use Renderer; 4 | use Rect; 5 | use color; 6 | use color::Color; 7 | use Ui; 8 | use ui::{ MouseButton, WidgetState, FocusedWidget, FocusedWidgetState }; 9 | 10 | use glutin; 11 | 12 | pub struct Knob { 13 | position: Rect, 14 | color: Color, 15 | state: KnobState, 16 | } 17 | 18 | pub enum KnobState { 19 | Idle, 20 | Hovered, 21 | Turning, 22 | } 23 | 24 | impl Knob { 25 | pub fn new(rect: Rect) -> Knob { 26 | Knob { 27 | position: rect, 28 | color: color::RED, 29 | state: KnobState::Idle, 30 | } 31 | } 32 | 33 | pub fn color(mut self, color: Color) -> Self { self.color = color; self } 34 | 35 | pub fn draw(self, renderer: &mut Renderer) -> Self { 36 | renderer.instructions.push( 37 | RenderElement::Circle(self.position.clone(), self.color) 38 | ); 39 | self 40 | } 41 | 42 | pub fn handle(mut self, events: &[glutin::Event], ui: &mut Ui, id: String) -> Self { 43 | let mouse_inside = ui.mouse_inside_rect(self.position.clone()); 44 | let is_focus = ui.is_focused(id.clone()); 45 | 46 | match ui.update(id, self.position.clone()) { 47 | Some(state) => { 48 | match state { 49 | FocusedWidgetState::Hovered => { self.color = color::BLUE; } 50 | FocusedWidgetState::Pressed => { 51 | println!("knob widget is turning"); 52 | self.state = KnobState::Turning; 53 | self.color = color::RED; 54 | } 55 | FocusedWidgetState::Activated => { 56 | println!("kknob widget stopped turning"); 57 | } 58 | } 59 | }, 60 | None => () 61 | }; 62 | 63 | 64 | // for event in events { 65 | // match event { 66 | // &glutin::Event::MouseInput(glutin::ElementState::Released, glutin::MouseButton::Left) => { 67 | // if mouse_inside { 68 | // self.state = KnobState::Idle; // we still need to care about this because we're processing a batch of events. 69 | // println!("knob widget stopped turning"); 70 | // } 71 | // } 72 | // _ => () 73 | // } 74 | // } 75 | 76 | self 77 | } 78 | 79 | pub fn changed(self) -> bool { 80 | false 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/widgets/mod.rs: -------------------------------------------------------------------------------- 1 | use glium::Frame; 2 | //use glium::backend::glutin_backend::GlutinFacade; 3 | use glium::backend::glutin::Display; 4 | 5 | //use Rect; 6 | 7 | mod knob; 8 | pub use self::knob::Knob; 9 | 10 | mod button; 11 | pub use self::button::Button; 12 | 13 | pub trait Widget { 14 | fn new(display: &Display) -> Self; 15 | fn draw(&self, target: &mut Frame); 16 | // fn position(&self) -> Rect; 17 | } 18 | --------------------------------------------------------------------------------