mirror of
https://github.com/FyroxEngine/Fyrox.git
synced 2026-10-12 02:02:20 +00:00
adding ui events
This commit is contained in:
Generated
-9
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user