adding ui events

This commit is contained in:
Dmitry
2019-08-02 21:59:02 +03:00
parent b4586f0202
commit 43b506b35c
3 changed files with 222 additions and 18 deletions
-9
View File
@@ -6,13 +6,8 @@
<component name="ChangeListManager">
<list default="true" id="1932b2c4-d6ff-413c-9558-19959667326b" name="Default Changelist" comment="scene graph">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/rg3d.exe" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/rg3d.iml" beforeDir="false" afterPath="$PROJECT_DIR$/rg3d.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/engine/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/engine/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/game/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/game/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/draw.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/draw.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/gui/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/gui/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/renderer/renderer.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/renderer/renderer.rs" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
@@ -37,10 +32,6 @@
<component name="MacroExpansionManager">
<option name="directoryName" value="KCIeWWoK" />
</component>
<component name="ProjectFrameBounds">
<option name="width" value="1280" />
<option name="height" value="1400" />
</component>
<component name="ProjectId" id="1OW00AGS1cNnZ78x9OeYDqnExos" />
<component name="PropertiesComponent">
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
+64 -5
View File
@@ -1,8 +1,9 @@
use crate::math::vec2::Vec2;
use crate::math::Rect;
use crate::gui::{Thickness, HorizontalAlignment, VerticalAlignment};
use crate::gui::{Thickness, HorizontalAlignment, VerticalAlignment, UINode};
use crate::resource::ttf::Font;
use std::os::raw::c_void;
use crate::utils::pool::Handle;
#[derive(Copy, Clone)]
pub struct Color {
@@ -39,6 +40,7 @@ impl Vertex {
}
}
#[derive(PartialEq)]
pub enum CommandKind {
Geometry,
Clip,
@@ -49,7 +51,7 @@ pub struct Command {
texture: u32,
index_offset: usize,
triangle_count: usize,
nesting: u8,
nesting: u8
}
impl Command {
@@ -437,6 +439,59 @@ impl DrawingContext {
}
}
#[inline]
pub fn get_commands(&self) -> &Vec<Command> {
&self.command_buffer
}
pub fn is_command_contains_point(&self, command: &Command, pos: &Vec2) -> bool {
let last = command.index_offset + command.triangle_count * 3;
/* check each triangle from command for intersection with mouse pointer */
for j in (command.index_offset..last).step_by(3) {
let va = match self.vertex_buffer.get(self.index_buffer[j] as usize) {
Some(v) => v,
None => return false
};
let vb = match self.vertex_buffer.get(self.index_buffer[j + 1] as usize) {
Some(v) => v,
None => return false
};
let vc = match self.vertex_buffer.get(self.index_buffer[j + 2] as usize) {
Some(v) => v,
None => return false
};
/* check if point is in triangle */
let v0 = vc.pos - va.pos;
let v1 = vb.pos - va.pos;
let v2 = *pos - va.pos;
let dot00 = v0.dot(v0);
let dot01 = v0.dot(v1);
let dot02 = v0.dot(v2);
let dot11 = v1.dot(v1);
let dot12 = v1.dot(v2);
let denom = dot00 * dot11 - dot01 * dot01;
if denom <= std::f32::EPSILON {
// We don't want floating-point exceptions
return false;
}
let invDenom = 1.0 / denom;
let u = (dot11 * dot02 - dot01 * dot12) * invDenom;
let v = (dot00 * dot12 - dot01 * dot02) * invDenom;
if (u >= 0.0) && (v >= 0.0) && (u + v < 1.0) {
return true;
}
}
return false;
}
pub fn push_line(&mut self, a: &Vec2, b: &Vec2, thickness: f32, color: Color) {
let perp = get_line_thickness_vector(a, b, thickness);
self.push_vertex(*a - perp, Vec2::make(0.0, 0.0), color);
@@ -500,7 +555,7 @@ impl DrawingContext {
self.push_triangle(index, index + 2, index + 3);
}
pub fn commit(&mut self, kind: CommandKind, texture: u32) {
pub fn commit(&mut self, kind: CommandKind, texture: u32) -> Option<usize> {
if self.triangles_to_commit > 0 {
let command = Command {
kind,
@@ -511,12 +566,16 @@ impl DrawingContext {
} else { 0 },
triangle_count: self.triangles_to_commit,
};
let index = self.command_buffer.len();
self.command_buffer.push(command);
self.triangles_to_commit = 0;
Some(index)
} else {
None
}
}
pub fn draw_text(&mut self, position: Vec2, formatted_text: &FormattedText) {
pub fn draw_text(&mut self, position: Vec2, formatted_text: &FormattedText) -> Option<usize> {
for element in formatted_text.glyphs.iter() {
let final_bounds = Rect::new(
position.x + element.bounds.x,
@@ -526,6 +585,6 @@ impl DrawingContext {
self.push_rect_filled(&final_bounds, Some(&element.tex_coords), element.color);
}
self.commit(CommandKind::Geometry, formatted_text.texture);
return self.commit(CommandKind::Geometry, formatted_text.texture);
}
}
+158 -4
View File
@@ -2,8 +2,8 @@ pub mod draw;
use crate::utils::pool::{Pool, Handle};
use crate::math::vec2::Vec2;
use glutin::{VirtualKeyCode, MouseButton};
use crate::gui::draw::{Color, DrawingContext, FormattedText, CommandKind, FormattedTextBuilder};
use glutin::{VirtualKeyCode, MouseButton, WindowEvent};
use crate::gui::draw::{Color, DrawingContext, FormattedText, CommandKind, FormattedTextBuilder, Command};
use crate::math::Rect;
use serde::export::PhantomData;
use crate::utils::rcpool::RcHandle;
@@ -94,6 +94,8 @@ pub struct Image {
texture: RcHandle<Resource>
}
pub struct Button {}
pub enum UINodeKind {
Base,
/// TODO
@@ -124,6 +126,16 @@ pub enum UINodeKind {
CheckBox,
}
#[derive(Copy, Clone, PartialEq)]
pub enum RoutedEventHandlerType {
MouseMove,
MouseEnter,
MouseLeave,
Count,
}
pub type EventHandler = dyn FnMut(&mut UserInterface, Handle<UINode>, &mut RoutedEvent);
pub struct UINode {
kind: UINodeKind,
/// Desired position relative to parent node
@@ -160,6 +172,10 @@ pub struct UINode {
visibility: Visibility,
children: Vec<Handle<UINode>>,
parent: Handle<UINode>,
/// Indices of commands in command buffer emitted by the node.
geometry: Vec<usize>,
is_mouse_over: bool,
event_handlers: [Option<Box<EventHandler>>; RoutedEventHandlerType::Count as usize],
}
pub enum RoutedEventKind {
@@ -187,6 +203,8 @@ pub enum RoutedEventKind {
pos: Vec2,
amount: u32,
},
MouseLeave,
MouseEnter
}
pub struct RoutedEvent {
@@ -200,6 +218,8 @@ pub struct UserInterface {
/// Every UI node will live on the window-sized canvas.
root_canvas: Handle<UINode>,
picked_node: Handle<UINode>,
prev_picked_node: Handle<UINode>,
}
#[inline]
@@ -273,6 +293,8 @@ impl UserInterface {
root_canvas: nodes.spawn(UINode::new(UINodeKind::Canvas)),
nodes,
drawing_context: DrawingContext::new(),
picked_node: Handle::none(),
prev_picked_node: Handle::none()
}
}
@@ -641,7 +663,9 @@ impl UserInterface {
node.actual_size.y);
self.drawing_context.push_rect_filled(&bounds, None, node.color);
self.drawing_context.push_rect_vary(&bounds, border.stroke_thickness, border.stroke_color);
self.drawing_context.commit(CommandKind::Geometry, 0);
if let Some(command_index) = self.drawing_context.commit(CommandKind::Geometry, 0) {
node.geometry.push(command_index);
}
}
UINodeKind::Image => {}
UINodeKind::Text(text) => {
@@ -657,7 +681,9 @@ impl UserInterface {
}
text.need_update = true; // TODO
}
self.drawing_context.draw_text(node.screen_position, text.formatted_text.as_ref().unwrap());
if let Some(command_index) = self.drawing_context.draw_text(node.screen_position, text.formatted_text.as_ref().unwrap()) {
node.geometry.push(command_index);
}
}
_ => ()
}
@@ -679,6 +705,131 @@ impl UserInterface {
&self.drawing_context
}
fn is_node_clipped(&self, node_handle: &Handle<UINode>, pt: &Vec2) -> bool {
let mut clipped = true;
if let Some(node) = self.nodes.borrow(node_handle) {
if node.visibility != Visibility::Visible {
return clipped;
}
for command_index in node.geometry.iter() {
if let Some(command) = self.drawing_context.get_commands().get(*command_index) {
if *command.get_kind() == CommandKind::Clip {
if self.drawing_context.is_command_contains_point(command, pt) {
clipped = false;
break;
}
}
}
}
// Point can be clipped by parent's clipping geometry.
if let Some(parent) = self.nodes.borrow(&node.parent) {
if !clipped {
clipped |= self.is_node_clipped(&node.parent, pt);
}
}
}
clipped
}
fn is_node_contains_point(&self, node_handle: &Handle<UINode>, pt: &Vec2) -> bool {
if let Some(node) = self.nodes.borrow(node_handle) {
if node.visibility != Visibility::Visible {
return false;
}
}
true
}
fn pick_node(&self, node_handle: &Handle<UINode>, pt: &Vec2, level: &mut i32) -> Handle<UINode> {
let mut picked = Handle::none();
let mut topmost_picked_level = 0;
if self.is_node_contains_point(node_handle, pt) {
picked = node_handle.clone();
topmost_picked_level = *level;
}
if let Some(node) = self.nodes.borrow(node_handle) {
for child_handle in node.children.iter() {
*level += 1;
let picked_child = self.pick_node(child_handle, pt, level);
if !picked_child.is_none() && *level > topmost_picked_level {
topmost_picked_level = *level;
picked = picked_child;
}
}
}
return picked;
}
pub fn hit_test(&self, pt: &Vec2) -> Handle<UINode> {
let mut level = 0;
self.pick_node(&self.root_canvas, pt, &mut level)
}
fn route_event(&mut self, node_handle: Handle<UINode>, event_type: RoutedEventHandlerType, event_args: &mut RoutedEvent) {
let mut handler = None;
let mut parent = Handle::none();
let index = event_type as usize;
if let Some(node) = self.nodes.borrow_mut(&node_handle) {
// Take event handler.
handler = node.event_handlers[index].take();
parent = node.parent.clone();
}
// Execute event handler.
if let Some(ref mut mouse_enter) = handler {
mouse_enter(self, node_handle.clone(), event_args);
}
if let Some(node) = self.nodes.borrow_mut(&node_handle) {
// Put event handler back.
node.event_handlers[index] = handler.take();
}
// Route event up on hierarchy (bubbling strategy) until is not handled.
if !event_args.handled {
self.route_event(parent, event_type, event_args);
}
}
pub fn process_event(&mut self, event: &glutin::WindowEvent) -> bool {
match event {
WindowEvent::CursorMoved { position, .. } => {
let pos = Vec2::make(position.x as f32, position.y as f32);
self.picked_node = self.hit_test(&pos);
// Fire mouse leave for previously picked node
if self.picked_node != self.prev_picked_node {
let mut route_mouse_leave = false;
if let Some(prev_picked_node) = self.nodes.borrow_mut(&self.prev_picked_node) {
if prev_picked_node.is_mouse_over {
prev_picked_node.is_mouse_over = false;
route_mouse_leave = true;
}
}
if route_mouse_leave {
let mut evt = RoutedEvent {
handled: false,
kind: RoutedEventKind::MouseLeave
};
self.route_event(self.prev_picked_node.clone(), RoutedEventHandlerType::MouseLeave, &mut evt);
}
}
}
_ => ()
}
false
}
}
impl UINode {
@@ -703,6 +854,9 @@ impl UINode {
visibility: Visibility::Visible,
children: Vec::new(),
parent: Handle::none(),
geometry: Vec::new(),
event_handlers: Default::default(),
is_mouse_over: false
}
}