mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
ui: initial flexbox support
This commit is contained in:
parent
bcf95a24db
commit
93bb1d5b8e
22 changed files with 619 additions and 379 deletions
|
@ -19,7 +19,7 @@ impl Default for App {
|
||||||
schedule: Default::default(),
|
schedule: Default::default(),
|
||||||
executor: Default::default(),
|
executor: Default::default(),
|
||||||
startup_schedule: Default::default(),
|
startup_schedule: Default::default(),
|
||||||
startup_executor: Default::default(),
|
startup_executor: ParallelExecutor::without_tracker_clears(),
|
||||||
runner: Box::new(run_once),
|
runner: Box::new(run_once),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex};
|
||||||
pub struct ParallelExecutor {
|
pub struct ParallelExecutor {
|
||||||
stages: Vec<ExecutorStage>,
|
stages: Vec<ExecutorStage>,
|
||||||
last_schedule_generation: usize,
|
last_schedule_generation: usize,
|
||||||
|
clear_trackers: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ParallelExecutor {
|
impl Default for ParallelExecutor {
|
||||||
|
@ -19,11 +20,18 @@ impl Default for ParallelExecutor {
|
||||||
Self {
|
Self {
|
||||||
stages: Default::default(),
|
stages: Default::default(),
|
||||||
last_schedule_generation: usize::MAX, // MAX forces prepare to run the first time
|
last_schedule_generation: usize::MAX, // MAX forces prepare to run the first time
|
||||||
|
clear_trackers: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParallelExecutor {
|
impl ParallelExecutor {
|
||||||
|
pub fn without_tracker_clears() -> Self {
|
||||||
|
Self {
|
||||||
|
clear_trackers: false,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn prepare(&mut self, schedule: &mut Schedule, world: &World) {
|
pub fn prepare(&mut self, schedule: &mut Schedule, world: &World) {
|
||||||
let schedule_generation = schedule.generation();
|
let schedule_generation = schedule.generation();
|
||||||
let schedule_changed = schedule_generation != self.last_schedule_generation;
|
let schedule_changed = schedule_generation != self.last_schedule_generation;
|
||||||
|
@ -53,7 +61,9 @@ impl ParallelExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
world.clear_trackers();
|
if self.clear_trackers {
|
||||||
|
world.clear_trackers();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use super::CameraProjection;
|
use super::CameraProjection;
|
||||||
|
use bevy_app::prelude::{EventReader, Events};
|
||||||
use bevy_ecs::{Component, Local, Query, Res};
|
use bevy_ecs::{Component, Local, Query, Res};
|
||||||
use bevy_math::Mat4;
|
use bevy_math::Mat4;
|
||||||
use bevy_property::Properties;
|
use bevy_property::Properties;
|
||||||
use bevy_window::{WindowCreated, WindowReference, WindowResized, Windows};
|
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
|
||||||
use bevy_app::prelude::{Events, EventReader};
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Properties)]
|
#[derive(Default, Debug, Properties)]
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
pub projection_matrix: Mat4,
|
pub projection_matrix: Mat4,
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
#[property(ignore)]
|
#[property(ignore)]
|
||||||
pub window: WindowReference,
|
pub window: WindowId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -27,7 +27,6 @@ pub fn camera_system<T: CameraProjection + Component>(
|
||||||
mut query: Query<(&mut Camera, &mut T)>,
|
mut query: Query<(&mut Camera, &mut T)>,
|
||||||
) {
|
) {
|
||||||
let mut changed_window_ids = Vec::new();
|
let mut changed_window_ids = Vec::new();
|
||||||
let mut changed_primary_window_id = None;
|
|
||||||
// handle resize events. latest events are handled first because we only want to resize each window once
|
// handle resize events. latest events are handled first because we only want to resize each window once
|
||||||
for event in state
|
for event in state
|
||||||
.window_resized_event_reader
|
.window_resized_event_reader
|
||||||
|
@ -38,11 +37,7 @@ pub fn camera_system<T: CameraProjection + Component>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.is_primary {
|
changed_window_ids.push(event.id);
|
||||||
changed_primary_window_id = Some(event.id);
|
|
||||||
} else {
|
|
||||||
changed_window_ids.push(event.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle resize events. latest events are handled first because we only want to resize each window once
|
// handle resize events. latest events are handled first because we only want to resize each window once
|
||||||
|
@ -55,30 +50,11 @@ pub fn camera_system<T: CameraProjection + Component>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.is_primary {
|
changed_window_ids.push(event.id);
|
||||||
changed_primary_window_id = Some(event.id);
|
|
||||||
} else {
|
|
||||||
changed_window_ids.push(event.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (mut camera, mut camera_projection) in &mut query.iter() {
|
for (mut camera, mut camera_projection) in &mut query.iter() {
|
||||||
if let Some(window) = match camera.window {
|
if let Some(window) = windows.get(camera.window) {
|
||||||
WindowReference::Id(id) => {
|
|
||||||
if changed_window_ids.contains(&id) {
|
|
||||||
windows.get(id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WindowReference::Primary => {
|
|
||||||
if let Some(id) = changed_primary_window_id {
|
|
||||||
windows.get(id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} {
|
|
||||||
camera_projection.update(window.width as usize, window.height as usize);
|
camera_projection.update(window.width as usize, window.height as usize);
|
||||||
camera.projection_matrix = camera_projection.get_projection_matrix();
|
camera.projection_matrix = camera_projection.get_projection_matrix();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
|
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
|
||||||
Color,
|
Color,
|
||||||
};
|
};
|
||||||
use bevy_window::WindowReference;
|
use bevy_window::WindowId;
|
||||||
|
|
||||||
pub struct BaseRenderGraphConfig {
|
pub struct BaseRenderGraphConfig {
|
||||||
pub add_2d_camera: bool,
|
pub add_2d_camera: bool,
|
||||||
|
@ -71,7 +71,7 @@ impl BaseRenderGraphBuilder for RenderGraph {
|
||||||
self.add_node(
|
self.add_node(
|
||||||
node::MAIN_DEPTH_TEXTURE,
|
node::MAIN_DEPTH_TEXTURE,
|
||||||
WindowTextureNode::new(
|
WindowTextureNode::new(
|
||||||
WindowReference::Primary,
|
WindowId::primary(),
|
||||||
TextureDescriptor {
|
TextureDescriptor {
|
||||||
size: Extent3d {
|
size: Extent3d {
|
||||||
depth: 1,
|
depth: 1,
|
||||||
|
@ -137,7 +137,7 @@ impl BaseRenderGraphBuilder for RenderGraph {
|
||||||
|
|
||||||
self.add_node(
|
self.add_node(
|
||||||
node::PRIMARY_SWAP_CHAIN,
|
node::PRIMARY_SWAP_CHAIN,
|
||||||
WindowSwapChainNode::new(WindowReference::Primary),
|
WindowSwapChainNode::new(WindowId::primary()),
|
||||||
);
|
);
|
||||||
|
|
||||||
if config.connect_main_pass_to_swapchain {
|
if config.connect_main_pass_to_swapchain {
|
||||||
|
|
|
@ -4,20 +4,20 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bevy_app::prelude::{EventReader, Events};
|
use bevy_app::prelude::{EventReader, Events};
|
||||||
use bevy_ecs::{Resources, World};
|
use bevy_ecs::{Resources, World};
|
||||||
use bevy_window::{WindowCreated, WindowReference, WindowResized, Windows};
|
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub struct WindowSwapChainNode {
|
pub struct WindowSwapChainNode {
|
||||||
window_reference: WindowReference,
|
window_id: WindowId,
|
||||||
window_created_event_reader: EventReader<WindowCreated>,
|
window_created_event_reader: EventReader<WindowCreated>,
|
||||||
window_resized_event_reader: EventReader<WindowResized>,
|
window_resized_event_reader: EventReader<WindowResized>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowSwapChainNode {
|
impl WindowSwapChainNode {
|
||||||
pub const OUT_TEXTURE: &'static str = "texture";
|
pub const OUT_TEXTURE: &'static str = "texture";
|
||||||
pub fn new(window_reference: WindowReference) -> Self {
|
pub fn new(window_id: WindowId) -> Self {
|
||||||
WindowSwapChainNode {
|
WindowSwapChainNode {
|
||||||
window_reference,
|
window_id,
|
||||||
window_created_event_reader: Default::default(),
|
window_created_event_reader: Default::default(),
|
||||||
window_resized_event_reader: Default::default(),
|
window_resized_event_reader: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,9 @@ impl Node for WindowSwapChainNode {
|
||||||
let window_resized_events = resources.get::<Events<WindowResized>>().unwrap();
|
let window_resized_events = resources.get::<Events<WindowResized>>().unwrap();
|
||||||
let windows = resources.get::<Windows>().unwrap();
|
let windows = resources.get::<Windows>().unwrap();
|
||||||
|
|
||||||
let window = match self.window_reference {
|
let window = windows
|
||||||
WindowReference::Primary => windows.get_primary().expect("No primary window exists"),
|
.get(self.window_id)
|
||||||
WindowReference::Id(id) => windows
|
.expect("Received window resized event for non-existent window");
|
||||||
.get(id)
|
|
||||||
.expect("Received window resized event for non-existent window"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let render_resource_context = render_context.resources_mut();
|
let render_resource_context = render_context.resources_mut();
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,11 @@ use crate::{
|
||||||
};
|
};
|
||||||
use bevy_app::prelude::{EventReader, Events};
|
use bevy_app::prelude::{EventReader, Events};
|
||||||
use bevy_ecs::{Resources, World};
|
use bevy_ecs::{Resources, World};
|
||||||
use bevy_window::{WindowCreated, WindowReference, WindowResized, Windows};
|
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
pub struct WindowTextureNode {
|
pub struct WindowTextureNode {
|
||||||
window_reference: WindowReference,
|
window_id: WindowId,
|
||||||
descriptor: TextureDescriptor,
|
descriptor: TextureDescriptor,
|
||||||
window_created_event_reader: EventReader<WindowCreated>,
|
window_created_event_reader: EventReader<WindowCreated>,
|
||||||
window_resized_event_reader: EventReader<WindowResized>,
|
window_resized_event_reader: EventReader<WindowResized>,
|
||||||
|
@ -17,9 +17,9 @@ pub struct WindowTextureNode {
|
||||||
|
|
||||||
impl WindowTextureNode {
|
impl WindowTextureNode {
|
||||||
pub const OUT_TEXTURE: &'static str = "texture";
|
pub const OUT_TEXTURE: &'static str = "texture";
|
||||||
pub fn new(window_reference: WindowReference, descriptor: TextureDescriptor) -> Self {
|
pub fn new(window_id: WindowId, descriptor: TextureDescriptor) -> Self {
|
||||||
WindowTextureNode {
|
WindowTextureNode {
|
||||||
window_reference,
|
window_id,
|
||||||
descriptor,
|
descriptor,
|
||||||
window_created_event_reader: Default::default(),
|
window_created_event_reader: Default::default(),
|
||||||
window_resized_event_reader: Default::default(),
|
window_resized_event_reader: Default::default(),
|
||||||
|
@ -49,12 +49,9 @@ impl Node for WindowTextureNode {
|
||||||
let window_resized_events = resources.get::<Events<WindowResized>>().unwrap();
|
let window_resized_events = resources.get::<Events<WindowResized>>().unwrap();
|
||||||
let windows = resources.get::<Windows>().unwrap();
|
let windows = resources.get::<Windows>().unwrap();
|
||||||
|
|
||||||
let window = match self.window_reference {
|
let window = windows
|
||||||
WindowReference::Primary => windows.get_primary().expect("No primary window exists"),
|
.get(self.window_id)
|
||||||
WindowReference::Id(id) => windows
|
.expect("Received window resized event for non-existent window");
|
||||||
.get(id)
|
|
||||||
.expect("Received window resized event for non-existent window"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.window_created_event_reader
|
.window_created_event_reader
|
||||||
|
|
|
@ -5,9 +5,9 @@ pub fn run_on_hierarchy<T, S>(
|
||||||
children_query: &Query<&Children>,
|
children_query: &Query<&Children>,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
parent_result: Option<&mut T>,
|
parent_result: Option<T>,
|
||||||
mut previous_result: Option<T>,
|
mut previous_result: Option<T>,
|
||||||
run: &mut dyn FnMut(&mut S, Entity, Option<&mut T>, Option<T>) -> Option<T>,
|
run: &mut dyn FnMut(&mut S, Entity, Option<T>, Option<T>) -> Option<T>,
|
||||||
) -> Option<T>
|
) -> Option<T>
|
||||||
where
|
where
|
||||||
T: Clone,
|
T: Clone,
|
||||||
|
@ -25,7 +25,7 @@ where
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut parent_result = run(state, entity, parent_result, previous_result);
|
let parent_result = run(state, entity, parent_result, previous_result);
|
||||||
previous_result = None;
|
previous_result = None;
|
||||||
if let Some(children) = children {
|
if let Some(children) = children {
|
||||||
for child in children {
|
for child in children {
|
||||||
|
@ -33,7 +33,7 @@ where
|
||||||
children_query,
|
children_query,
|
||||||
state,
|
state,
|
||||||
child,
|
child,
|
||||||
parent_result.as_mut(),
|
parent_result.clone(),
|
||||||
previous_result,
|
previous_result,
|
||||||
run,
|
run,
|
||||||
);
|
);
|
||||||
|
|
|
@ -19,3 +19,5 @@ bevy_render = { path = "../bevy_render" }
|
||||||
bevy_window = { path = "../bevy_window" }
|
bevy_window = { path = "../bevy_window" }
|
||||||
|
|
||||||
bevy_math = { path = "../bevy_math" }
|
bevy_math = { path = "../bevy_math" }
|
||||||
|
|
||||||
|
stretch = "0.3"
|
|
@ -1,22 +1,29 @@
|
||||||
use super::Node;
|
use super::Node;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
prelude::Flex,
|
||||||
render::UI_PIPELINE_HANDLE,
|
render::UI_PIPELINE_HANDLE,
|
||||||
widget::{Button, Text},
|
widget::{Button, Text},
|
||||||
Click, FocusPolicy, Hover,
|
Click, FlexSurfaceId, FocusPolicy, Hover,
|
||||||
};
|
};
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_ecs::Bundle;
|
use bevy_ecs::Bundle;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
|
camera::{Camera, OrthographicProjection, VisibleEntities, WindowOrigin},
|
||||||
draw::Draw,
|
draw::Draw,
|
||||||
mesh::Mesh,
|
mesh::Mesh,
|
||||||
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines},
|
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines},
|
||||||
};
|
};
|
||||||
use bevy_sprite::{ColorMaterial, QUAD_HANDLE};
|
use bevy_sprite::{ColorMaterial, QUAD_HANDLE};
|
||||||
use bevy_transform::{components::LocalTransform, prelude::Transform};
|
use bevy_transform::{
|
||||||
|
components::LocalTransform,
|
||||||
|
prelude::{Rotation, Scale, Transform, Translation},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Bundle)]
|
#[derive(Bundle)]
|
||||||
pub struct NodeComponents {
|
pub struct NodeComponents {
|
||||||
pub node: Node,
|
pub node: Node,
|
||||||
|
pub flex: Flex,
|
||||||
|
pub flex_surface_id: FlexSurfaceId,
|
||||||
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
|
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
|
||||||
pub material: Handle<ColorMaterial>,
|
pub material: Handle<ColorMaterial>,
|
||||||
pub draw: Draw,
|
pub draw: Draw,
|
||||||
|
@ -48,6 +55,8 @@ impl Default for NodeComponents {
|
||||||
},
|
},
|
||||||
)]),
|
)]),
|
||||||
node: Default::default(),
|
node: Default::default(),
|
||||||
|
flex_surface_id: Default::default(),
|
||||||
|
flex: Flex::default(),
|
||||||
material: Default::default(),
|
material: Default::default(),
|
||||||
draw: Default::default(),
|
draw: Default::default(),
|
||||||
transform: Default::default(),
|
transform: Default::default(),
|
||||||
|
@ -59,6 +68,8 @@ impl Default for NodeComponents {
|
||||||
#[derive(Bundle)]
|
#[derive(Bundle)]
|
||||||
pub struct TextComponents {
|
pub struct TextComponents {
|
||||||
pub node: Node,
|
pub node: Node,
|
||||||
|
pub flex: Flex,
|
||||||
|
pub flex_surface_id: FlexSurfaceId,
|
||||||
pub draw: Draw,
|
pub draw: Draw,
|
||||||
pub text: Text,
|
pub text: Text,
|
||||||
pub focus_policy: FocusPolicy,
|
pub focus_policy: FocusPolicy,
|
||||||
|
@ -71,6 +82,8 @@ impl Default for TextComponents {
|
||||||
TextComponents {
|
TextComponents {
|
||||||
text: Text::default(),
|
text: Text::default(),
|
||||||
node: Default::default(),
|
node: Default::default(),
|
||||||
|
flex: Flex::default(),
|
||||||
|
flex_surface_id: Default::default(),
|
||||||
focus_policy: FocusPolicy::Pass,
|
focus_policy: FocusPolicy::Pass,
|
||||||
draw: Draw {
|
draw: Draw {
|
||||||
is_transparent: true,
|
is_transparent: true,
|
||||||
|
@ -86,6 +99,8 @@ impl Default for TextComponents {
|
||||||
pub struct ButtonComponents {
|
pub struct ButtonComponents {
|
||||||
pub node: Node,
|
pub node: Node,
|
||||||
pub button: Button,
|
pub button: Button,
|
||||||
|
pub flex: Flex,
|
||||||
|
pub flex_surface_id: FlexSurfaceId,
|
||||||
pub click: Click,
|
pub click: Click,
|
||||||
pub hover: Hover,
|
pub hover: Hover,
|
||||||
pub focus_policy: FocusPolicy,
|
pub focus_policy: FocusPolicy,
|
||||||
|
@ -124,6 +139,8 @@ impl Default for ButtonComponents {
|
||||||
},
|
},
|
||||||
)]),
|
)]),
|
||||||
node: Default::default(),
|
node: Default::default(),
|
||||||
|
flex_surface_id: Default::default(),
|
||||||
|
flex: Flex::default(),
|
||||||
material: Default::default(),
|
material: Default::default(),
|
||||||
draw: Default::default(),
|
draw: Default::default(),
|
||||||
transform: Default::default(),
|
transform: Default::default(),
|
||||||
|
@ -131,3 +148,38 @@ impl Default for ButtonComponents {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Bundle)]
|
||||||
|
pub struct UiCameraComponents {
|
||||||
|
pub camera: Camera,
|
||||||
|
pub orthographic_projection: OrthographicProjection,
|
||||||
|
pub visible_entities: VisibleEntities,
|
||||||
|
pub transform: Transform,
|
||||||
|
pub translation: Translation,
|
||||||
|
pub rotation: Rotation,
|
||||||
|
pub scale: Scale,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for UiCameraComponents {
|
||||||
|
fn default() -> Self {
|
||||||
|
// we want 0 to be "closest" and +far to be "farthest" in 2d, so we offset
|
||||||
|
// the camera's translation by far and use a right handed coordinate system
|
||||||
|
let far = 1000.0;
|
||||||
|
UiCameraComponents {
|
||||||
|
camera: Camera {
|
||||||
|
name: Some(crate::camera::UI_CAMERA.to_string()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
orthographic_projection: OrthographicProjection {
|
||||||
|
far,
|
||||||
|
window_origin: WindowOrigin::BottomLeft,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
visible_entities: Default::default(),
|
||||||
|
transform: Default::default(),
|
||||||
|
translation: Translation::new(0.0, 0.0, far - 0.1),
|
||||||
|
rotation: Default::default(),
|
||||||
|
scale: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
196
crates/bevy_ui/src/flex.rs
Normal file
196
crates/bevy_ui/src/flex.rs
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
use crate::Node;
|
||||||
|
use bevy_ecs::{Changed, Entity, Query, Res, ResMut, With, Without};
|
||||||
|
use bevy_math::Vec2;
|
||||||
|
use bevy_transform::prelude::{Children, LocalTransform, Parent};
|
||||||
|
use bevy_window::Windows;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use stretch::{
|
||||||
|
geometry::Size,
|
||||||
|
number::Number,
|
||||||
|
result::Layout,
|
||||||
|
style::{Dimension, PositionType, Style},
|
||||||
|
Stretch,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone, Hash, Eq, PartialEq)]
|
||||||
|
pub struct FlexSurfaceId(usize);
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct FlexSurfaces {
|
||||||
|
surfaces: HashMap<FlexSurfaceId, FlexSurface>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FlexSurface {
|
||||||
|
entity_to_stretch: HashMap<Entity, stretch::node::Node>,
|
||||||
|
stretch_to_entity: HashMap<stretch::node::Node, Entity>,
|
||||||
|
surface_root_node: stretch::node::Node,
|
||||||
|
size: Vec2,
|
||||||
|
stretch: Stretch,
|
||||||
|
orphans: HashSet<Entity>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlexSurface {
|
||||||
|
fn new() -> Self {
|
||||||
|
let mut stretch = Stretch::new();
|
||||||
|
let surface_root_node = stretch
|
||||||
|
.new_node(
|
||||||
|
Style {
|
||||||
|
size: Size {
|
||||||
|
width: Dimension::Percent(1.0),
|
||||||
|
height: Dimension::Percent(1.0),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Self {
|
||||||
|
entity_to_stretch: Default::default(),
|
||||||
|
stretch_to_entity: Default::default(),
|
||||||
|
orphans: Default::default(),
|
||||||
|
size: Default::default(),
|
||||||
|
stretch,
|
||||||
|
surface_root_node,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upsert_node(&mut self, entity: Entity, style: &Style, orphan: bool) {
|
||||||
|
let mut added = false;
|
||||||
|
let stretch = &mut self.stretch;
|
||||||
|
let stretch_to_entity = &mut self.stretch_to_entity;
|
||||||
|
let stretch_node = self.entity_to_stretch.entry(entity).or_insert_with(|| {
|
||||||
|
added = true;
|
||||||
|
let stretch_node = stretch.new_node(style.clone(), Vec::new()).unwrap();
|
||||||
|
stretch_to_entity.insert(stretch_node, entity);
|
||||||
|
stretch_node
|
||||||
|
});
|
||||||
|
|
||||||
|
if !added {
|
||||||
|
self.stretch
|
||||||
|
.set_style(*stretch_node, style.clone())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if orphan && !self.orphans.contains(&entity) {
|
||||||
|
self.stretch
|
||||||
|
.add_child(self.surface_root_node, *stretch_node)
|
||||||
|
.unwrap();
|
||||||
|
self.orphans.insert(entity);
|
||||||
|
} else if !orphan && self.orphans.contains(&entity) {
|
||||||
|
self.stretch
|
||||||
|
.remove_child(self.surface_root_node, *stretch_node)
|
||||||
|
.unwrap();
|
||||||
|
self.orphans.remove(&entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_children(&mut self, entity: Entity, children: &Children) {
|
||||||
|
let mut stretch_children = Vec::with_capacity(children.len());
|
||||||
|
for child in children.iter() {
|
||||||
|
let stretch_node = self.entity_to_stretch.get(child).unwrap();
|
||||||
|
stretch_children.push(*stretch_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
let stretch_node = self.entity_to_stretch.get(&entity).unwrap();
|
||||||
|
|
||||||
|
self.stretch
|
||||||
|
.set_children(*stretch_node, stretch_children)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_layout(&mut self) {
|
||||||
|
self.stretch
|
||||||
|
.compute_layout(
|
||||||
|
self.surface_root_node,
|
||||||
|
stretch::geometry::Size {
|
||||||
|
width: Number::Defined(self.size.x()),
|
||||||
|
height: Number::Defined(self.size.y()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_layout(&self, entity: Entity) -> Result<&Layout, stretch::Error> {
|
||||||
|
let stretch_node = self.entity_to_stretch.get(&entity).unwrap();
|
||||||
|
self.stretch.layout(*stretch_node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFE: as long as MeasureFunc is Send + Sync. https://github.com/vislyhq/stretch/issues/69
|
||||||
|
unsafe impl Send for FlexSurfaces {}
|
||||||
|
unsafe impl Sync for FlexSurfaces {}
|
||||||
|
|
||||||
|
pub fn primary_window_flex_surface_system(
|
||||||
|
windows: Res<Windows>,
|
||||||
|
mut flex_surfaces: ResMut<FlexSurfaces>,
|
||||||
|
) {
|
||||||
|
if let Some(surface) = flex_surfaces.surfaces.get_mut(&FlexSurfaceId::default()) {
|
||||||
|
if let Some(window) = windows.get_primary() {
|
||||||
|
surface.size = Vec2::new(window.width as f32, window.height as f32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flex_node_system(
|
||||||
|
mut flex_surfaces: ResMut<FlexSurfaces>,
|
||||||
|
mut root_node_query: Query<With<Node, Without<Parent, (&FlexSurfaceId, &mut Style)>>>,
|
||||||
|
mut node_query: Query<With<Node, (Entity, &FlexSurfaceId, Changed<Style>, Option<&Parent>)>>,
|
||||||
|
mut children_query: Query<With<Node, (Entity, &FlexSurfaceId, Changed<Children>)>>,
|
||||||
|
mut node_transform_query: Query<(
|
||||||
|
Entity,
|
||||||
|
&mut Node,
|
||||||
|
&FlexSurfaceId,
|
||||||
|
&mut LocalTransform,
|
||||||
|
Option<&Parent>,
|
||||||
|
)>,
|
||||||
|
) {
|
||||||
|
// initialize stretch hierarchies
|
||||||
|
for (flex_surface_id, mut style) in &mut root_node_query.iter() {
|
||||||
|
flex_surfaces
|
||||||
|
.surfaces
|
||||||
|
.entry(*flex_surface_id)
|
||||||
|
.or_insert_with(|| FlexSurface::new());
|
||||||
|
|
||||||
|
// root nodes should not be positioned relative to each other
|
||||||
|
style.position_type = PositionType::Absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: cleanup unused surfaces
|
||||||
|
|
||||||
|
// update changed nodes
|
||||||
|
for (entity, flex_surface_id, style, parent) in &mut node_query.iter() {
|
||||||
|
// TODO: remove node from old hierarchy if its root has changed
|
||||||
|
let surface = flex_surfaces.surfaces.get_mut(flex_surface_id).unwrap();
|
||||||
|
surface.upsert_node(entity, &style, parent.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle removed nodes
|
||||||
|
|
||||||
|
// update children
|
||||||
|
for (entity, flex_surface_id, children) in &mut children_query.iter() {
|
||||||
|
let surface = flex_surfaces.surfaces.get_mut(flex_surface_id).unwrap();
|
||||||
|
surface.update_children(entity, &children);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute layouts
|
||||||
|
for surface in flex_surfaces.surfaces.values_mut() {
|
||||||
|
surface.compute_layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entity, mut node, flex_surface_id, mut local, parent) in &mut node_transform_query.iter() {
|
||||||
|
let surface = flex_surfaces.surfaces.get_mut(flex_surface_id).unwrap();
|
||||||
|
let layout = surface.get_layout(entity).unwrap();
|
||||||
|
node.size = Vec2::new(layout.size.width, layout.size.height);
|
||||||
|
let mut position = local.w_axis();
|
||||||
|
position.set_x(layout.location.x + layout.size.width / 2.0);
|
||||||
|
position.set_y(layout.location.y + layout.size.height / 2.0);
|
||||||
|
if let Some(parent) = parent {
|
||||||
|
if let Ok(parent_layout) = surface.get_layout(parent.0) {
|
||||||
|
*position.x_mut() -= parent_layout.size.width / 2.0;
|
||||||
|
*position.y_mut() -= parent_layout.size.height / 2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local.set_w_axis(position);
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,7 +52,6 @@ pub struct State {
|
||||||
|
|
||||||
pub fn ui_focus_system(
|
pub fn ui_focus_system(
|
||||||
mut state: Local<State>,
|
mut state: Local<State>,
|
||||||
windows: Res<Windows>,
|
|
||||||
mouse_button_input: Res<Input<MouseButton>>,
|
mouse_button_input: Res<Input<MouseButton>>,
|
||||||
cursor_moved_events: Res<Events<CursorMoved>>,
|
cursor_moved_events: Res<Events<CursorMoved>>,
|
||||||
mut node_query: Query<(
|
mut node_query: Query<(
|
||||||
|
@ -79,7 +78,6 @@ pub fn ui_focus_system(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mouse_clicked = mouse_button_input.just_pressed(MouseButton::Left);
|
let mouse_clicked = mouse_button_input.just_pressed(MouseButton::Left);
|
||||||
let window = windows.get_primary().unwrap();
|
|
||||||
let mut hovered_entity = None;
|
let mut hovered_entity = None;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -88,9 +86,7 @@ pub fn ui_focus_system(
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(entity, node, transform, click, hover, focus_policy)| {
|
.filter_map(|(entity, node, transform, click, hover, focus_policy)| {
|
||||||
let position = transform.value.w_axis();
|
let position = transform.value.w_axis();
|
||||||
// TODO: ui transform is currently in world space, so we need to move it to ui space. we should make these transforms ui space
|
let ui_position = position.truncate().truncate();
|
||||||
let ui_position = position.truncate().truncate()
|
|
||||||
+ Vec2::new(window.width as f32 / 2.0, window.height as f32 / 2.0);
|
|
||||||
let extents = node.size / 2.0;
|
let extents = node.size / 2.0;
|
||||||
let min = ui_position - extents;
|
let min = ui_position - extents;
|
||||||
let max = ui_position + extents;
|
let max = ui_position + extents;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod anchors;
|
mod anchors;
|
||||||
mod focus;
|
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
|
mod flex;
|
||||||
|
mod focus;
|
||||||
mod margins;
|
mod margins;
|
||||||
mod node;
|
mod node;
|
||||||
mod render;
|
mod render;
|
||||||
|
@ -8,6 +9,7 @@ pub mod update;
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
|
|
||||||
pub use anchors::*;
|
pub use anchors::*;
|
||||||
|
pub use flex::*;
|
||||||
pub use focus::*;
|
pub use focus::*;
|
||||||
pub use margins::*;
|
pub use margins::*;
|
||||||
pub use node::*;
|
pub use node::*;
|
||||||
|
@ -19,21 +21,32 @@ pub mod prelude {
|
||||||
widget::{Button, Text},
|
widget::{Button, Text},
|
||||||
Anchors, Click, Hover, Margins, Node,
|
Anchors, Click, Hover, Margins, Node,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use stretch::{
|
||||||
|
geometry::{Point, Rect, Size},
|
||||||
|
style::{Style as Flex, *},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::IntoQuerySystem;
|
use bevy_ecs::IntoQuerySystem;
|
||||||
use bevy_render::render_graph::RenderGraph;
|
use bevy_render::render_graph::RenderGraph;
|
||||||
use update::ui_update_system;
|
use update::ui_z_system;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct UiPlugin;
|
pub struct UiPlugin;
|
||||||
|
|
||||||
impl AppPlugin for UiPlugin {
|
impl AppPlugin for UiPlugin {
|
||||||
fn build(&self, app: &mut AppBuilder) {
|
fn build(&self, app: &mut AppBuilder) {
|
||||||
app.add_system_to_stage(stage::PRE_UPDATE, ui_focus_system.system())
|
app.init_resource::<FlexSurfaces>()
|
||||||
// must run before transform update systems
|
.add_system_to_stage(stage::PRE_UPDATE, ui_focus_system.system())
|
||||||
.add_system_to_stage_front(stage::POST_UPDATE, ui_update_system.system())
|
// add these stages to front because these must run before transform update systems
|
||||||
|
.add_system_to_stage_front(stage::POST_UPDATE, flex_node_system.system())
|
||||||
|
.add_system_to_stage_front(stage::POST_UPDATE, ui_z_system.system())
|
||||||
|
.add_system_to_stage_front(
|
||||||
|
stage::POST_UPDATE,
|
||||||
|
primary_window_flex_surface_system.system(),
|
||||||
|
)
|
||||||
.add_system_to_stage(stage::POST_UPDATE, widget::text_system.system())
|
.add_system_to_stage(stage::POST_UPDATE, widget::text_system.system())
|
||||||
.add_system_to_stage(bevy_render::stage::DRAW, widget::draw_text_system.system());
|
.add_system_to_stage(bevy_render::stage::DRAW, widget::draw_text_system.system());
|
||||||
|
|
||||||
|
|
|
@ -1,108 +1,7 @@
|
||||||
use super::{Anchors, Margins};
|
use bevy_math::Vec2;
|
||||||
use bevy_math::{Mat4, Vec2, Vec3};
|
|
||||||
use bevy_render::renderer::RenderResources;
|
use bevy_render::renderer::RenderResources;
|
||||||
use bevy_transform::components::LocalTransform;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
enum MarginGrowDirection {
|
|
||||||
Negative,
|
|
||||||
Positive,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, RenderResources)]
|
#[derive(Debug, Clone, Default, RenderResources)]
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
pub size: Vec2,
|
pub size: Vec2,
|
||||||
#[render_resources(ignore)]
|
|
||||||
pub position: Vec2,
|
|
||||||
#[render_resources(ignore)]
|
|
||||||
pub anchors: Anchors,
|
|
||||||
#[render_resources(ignore)]
|
|
||||||
pub margins: Margins,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node {
|
|
||||||
pub fn new(anchors: Anchors, margins: Margins) -> Self {
|
|
||||||
Node {
|
|
||||||
anchors,
|
|
||||||
margins,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn positioned(position: Vec2, anchors: Anchors, margins: Margins) -> Self {
|
|
||||||
Node {
|
|
||||||
position,
|
|
||||||
anchors,
|
|
||||||
margins,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(
|
|
||||||
&mut self,
|
|
||||||
local_transform: &mut LocalTransform,
|
|
||||||
z_offset: f32,
|
|
||||||
parent_size: Vec2,
|
|
||||||
) {
|
|
||||||
let (quad_x, quad_width) = Self::compute_dimension_properties(
|
|
||||||
self.margins.left,
|
|
||||||
self.margins.right,
|
|
||||||
self.anchors.left,
|
|
||||||
self.anchors.right,
|
|
||||||
parent_size.x(),
|
|
||||||
);
|
|
||||||
let (quad_y, quad_height) = Self::compute_dimension_properties(
|
|
||||||
self.margins.bottom,
|
|
||||||
self.margins.top,
|
|
||||||
self.anchors.bottom,
|
|
||||||
self.anchors.top,
|
|
||||||
parent_size.y(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.size = Vec2::new(quad_width, quad_height);
|
|
||||||
local_transform.0 = Mat4::from_translation(
|
|
||||||
self.position.extend(z_offset) + Vec3::new(quad_x, quad_y, z_offset)
|
|
||||||
- (parent_size / 2.0).extend(0.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_dimension_properties(
|
|
||||||
margin0: f32,
|
|
||||||
margin1: f32,
|
|
||||||
anchor0: f32,
|
|
||||||
anchor1: f32,
|
|
||||||
length: f32,
|
|
||||||
) -> (f32, f32) {
|
|
||||||
let anchor_p0 = anchor0 * length;
|
|
||||||
let anchor_p1 = anchor1 * length;
|
|
||||||
|
|
||||||
let p0_grow_direction = if anchor_p0 <= 0.5 {
|
|
||||||
MarginGrowDirection::Positive
|
|
||||||
} else {
|
|
||||||
MarginGrowDirection::Negative
|
|
||||||
};
|
|
||||||
let p1_grow_direction = if anchor_p1 <= 0.5 {
|
|
||||||
MarginGrowDirection::Positive
|
|
||||||
} else {
|
|
||||||
MarginGrowDirection::Negative
|
|
||||||
};
|
|
||||||
|
|
||||||
let p0 = Self::compute_anchored_position(margin0, anchor_p0, p0_grow_direction);
|
|
||||||
let p1 = Self::compute_anchored_position(margin1, anchor_p1, p1_grow_direction);
|
|
||||||
|
|
||||||
let final_width = p1 - p0;
|
|
||||||
let p = (p0 + p1) / 2.0;
|
|
||||||
(p, final_width.abs())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_anchored_position(
|
|
||||||
margin: f32,
|
|
||||||
anchor_position: f32,
|
|
||||||
grow_direction: MarginGrowDirection,
|
|
||||||
) -> f32 {
|
|
||||||
match grow_direction {
|
|
||||||
MarginGrowDirection::Negative => anchor_position - margin,
|
|
||||||
MarginGrowDirection::Positive => anchor_position + margin,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,75 +1,59 @@
|
||||||
use super::Node;
|
use super::Node;
|
||||||
use bevy_ecs::{Entity, Query, Res, Without};
|
use crate::FlexSurfaceId;
|
||||||
use bevy_math::Vec2;
|
use bevy_ecs::{Entity, Query, With, Without};
|
||||||
use bevy_transform::{
|
use bevy_transform::{
|
||||||
hierarchy,
|
hierarchy,
|
||||||
prelude::{Children, LocalTransform, Parent},
|
prelude::{Children, LocalTransform, Parent},
|
||||||
};
|
};
|
||||||
use bevy_window::Windows;
|
|
||||||
|
|
||||||
pub const UI_Z_STEP: f32 = 0.001;
|
pub const UI_Z_STEP: f32 = 0.001;
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub fn ui_z_system(
|
||||||
pub struct Rect {
|
mut root_node_query: Query<With<Node, Without<Parent, (Entity, &FlexSurfaceId)>>>,
|
||||||
pub z: f32,
|
mut node_query: Query<(Entity, &Node, &mut FlexSurfaceId, &mut LocalTransform)>,
|
||||||
pub size: Vec2,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ui_update_system(
|
|
||||||
windows: Res<Windows>,
|
|
||||||
mut orphan_node_query: Query<Without<Parent, (Entity, &mut Node, &mut LocalTransform)>>,
|
|
||||||
mut node_query: Query<(Entity, &mut Node, &mut LocalTransform)>,
|
|
||||||
children_query: Query<&Children>,
|
children_query: Query<&Children>,
|
||||||
) {
|
) {
|
||||||
let window_size = if let Some(window) = windows.get_primary() {
|
let mut window_z = 0.0;
|
||||||
Vec2::new(window.width as f32, window.height as f32)
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let orphan_nodes = orphan_node_query
|
|
||||||
.iter()
|
|
||||||
.iter()
|
|
||||||
.map(|(e, _, _)| e)
|
|
||||||
.collect::<Vec<Entity>>();
|
|
||||||
let mut window_rect = Rect {
|
|
||||||
z: 0.0,
|
|
||||||
size: window_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut previous_sibling_result = Some(Rect {
|
// PERF: we can probably avoid an allocation here by making root_node_query and node_query non-overlapping
|
||||||
z: 0.0,
|
let root_nodes = (&mut root_node_query.iter())
|
||||||
size: window_size,
|
.iter()
|
||||||
});
|
.map(|(e, s)| (e, *s))
|
||||||
for entity in orphan_nodes {
|
.collect::<Vec<(Entity, FlexSurfaceId)>>();
|
||||||
previous_sibling_result = hierarchy::run_on_hierarchy(
|
|
||||||
|
for (entity, flex_surface_id) in root_nodes {
|
||||||
|
if let Some(result) = hierarchy::run_on_hierarchy(
|
||||||
&children_query,
|
&children_query,
|
||||||
&mut node_query,
|
&mut node_query,
|
||||||
entity,
|
entity,
|
||||||
Some(&mut window_rect),
|
Some((flex_surface_id, window_z)),
|
||||||
previous_sibling_result,
|
Some((flex_surface_id, window_z)),
|
||||||
&mut update_node_entity,
|
&mut update_node_entity,
|
||||||
);
|
) {
|
||||||
|
window_z = result.1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_node_entity(
|
fn update_node_entity(
|
||||||
node_query: &mut Query<(Entity, &mut Node, &mut LocalTransform)>,
|
node_query: &mut Query<(Entity, &Node, &mut FlexSurfaceId, &mut LocalTransform)>,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
parent_rect: Option<&mut Rect>,
|
parent_result: Option<(FlexSurfaceId, f32)>,
|
||||||
previous_rect: Option<Rect>,
|
previous_result: Option<(FlexSurfaceId, f32)>,
|
||||||
) -> Option<Rect> {
|
) -> Option<(FlexSurfaceId, f32)> {
|
||||||
if let Ok(mut node) = node_query.get_mut::<Node>(entity) {
|
let mut surface_id = node_query.get_mut::<FlexSurfaceId>(entity).unwrap();
|
||||||
if let Ok(mut local_transform) = node_query.get_mut::<LocalTransform>(entity) {
|
let mut transform = node_query.get_mut::<LocalTransform>(entity).unwrap();
|
||||||
let parent_rect = parent_rect.unwrap();
|
let (parent_surface_id, _) = parent_result?;
|
||||||
let mut z = UI_Z_STEP;
|
let mut z = UI_Z_STEP;
|
||||||
if let Some(previous_rect) = previous_rect {
|
if let Some((_, previous_z)) = previous_result {
|
||||||
z += previous_rect.z
|
z += previous_z;
|
||||||
};
|
};
|
||||||
|
|
||||||
node.update(&mut local_transform, z, parent_rect.size);
|
let mut position = transform.w_axis();
|
||||||
return Some(Rect { size: node.size, z });
|
position.set_z(z);
|
||||||
}
|
transform.set_w_axis(position);
|
||||||
}
|
|
||||||
|
|
||||||
None
|
*surface_id = parent_surface_id;
|
||||||
|
|
||||||
|
return Some((parent_surface_id, z));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ pub struct WindowResized {
|
||||||
pub id: WindowId,
|
pub id: WindowId,
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
pub height: usize,
|
pub height: usize,
|
||||||
pub is_primary: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An event that indicates that a new window should be created.
|
/// An event that indicates that a new window should be created.
|
||||||
|
@ -27,7 +26,6 @@ pub struct CloseWindow {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WindowCreated {
|
pub struct WindowCreated {
|
||||||
pub id: WindowId,
|
pub id: WindowId,
|
||||||
pub is_primary: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An event that is sent whenever a close was requested for a window. For example: when the "close" button
|
/// An event that is sent whenever a close was requested for a window. For example: when the "close" button
|
||||||
|
@ -35,7 +33,6 @@ pub struct WindowCreated {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WindowCloseRequested {
|
pub struct WindowCloseRequested {
|
||||||
pub id: WindowId,
|
pub id: WindowId,
|
||||||
pub is_primary: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl AppPlugin for WindowPlugin {
|
||||||
.unwrap_or_else(|| WindowDescriptor::default());
|
.unwrap_or_else(|| WindowDescriptor::default());
|
||||||
let mut create_window_event = resources.get_mut::<Events<CreateWindow>>().unwrap();
|
let mut create_window_event = resources.get_mut::<Events<CreateWindow>>().unwrap();
|
||||||
create_window_event.send(CreateWindow {
|
create_window_event.send(CreateWindow {
|
||||||
id: WindowId::new(),
|
id: WindowId::primary(),
|
||||||
descriptor: window_descriptor.clone(),
|
descriptor: window_descriptor.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,5 @@
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum WindowReference {
|
|
||||||
Primary,
|
|
||||||
Id(WindowId),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for WindowReference {
|
|
||||||
fn default() -> Self {
|
|
||||||
WindowReference::Primary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct WindowId(Uuid);
|
pub struct WindowId(Uuid);
|
||||||
|
|
||||||
|
@ -20,11 +8,25 @@ impl WindowId {
|
||||||
WindowId(Uuid::new_v4())
|
WindowId(Uuid::new_v4())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn primary() -> Self {
|
||||||
|
WindowId(Uuid::from_u128(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_primary(&self) -> bool {
|
||||||
|
*self == WindowId::primary()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
self.0.to_simple().to_string()
|
self.0.to_simple().to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for WindowId {
|
||||||
|
fn default() -> Self {
|
||||||
|
WindowId::primary()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
pub id: WindowId,
|
pub id: WindowId,
|
||||||
|
|
|
@ -4,15 +4,10 @@ use std::collections::HashMap;
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Windows {
|
pub struct Windows {
|
||||||
windows: HashMap<WindowId, Window>,
|
windows: HashMap<WindowId, Window>,
|
||||||
primary_window: Option<WindowId>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Windows {
|
impl Windows {
|
||||||
pub fn add(&mut self, window: Window) {
|
pub fn add(&mut self, window: Window) {
|
||||||
if let None = self.primary_window {
|
|
||||||
self.primary_window = Some(window.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.windows.insert(window.id, window);
|
self.windows.insert(window.id, window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,14 +20,7 @@ impl Windows {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_primary(&self) -> Option<&Window> {
|
pub fn get_primary(&self) -> Option<&Window> {
|
||||||
self.primary_window
|
self.get(WindowId::primary())
|
||||||
.and_then(|primary| self.windows.get(&primary))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_primary(&self, window_id: WindowId) -> bool {
|
|
||||||
self.get_primary()
|
|
||||||
.map(|primary_window| primary_window.id == window_id)
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &Window> {
|
pub fn iter(&self) -> impl Iterator<Item = &Window> {
|
||||||
|
|
|
@ -76,7 +76,6 @@ pub fn winit_runner(mut app: App) {
|
||||||
id: window_id,
|
id: window_id,
|
||||||
height: window.height as usize,
|
height: window.height as usize,
|
||||||
width: window.width as usize,
|
width: window.width as usize,
|
||||||
is_primary: windows.is_primary(window_id),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
event::Event::WindowEvent {
|
event::Event::WindowEvent {
|
||||||
|
@ -94,7 +93,6 @@ pub fn winit_runner(mut app: App) {
|
||||||
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
|
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
|
||||||
window_close_requested_events.send(WindowCloseRequested {
|
window_close_requested_events.send(WindowCloseRequested {
|
||||||
id: window_id,
|
id: window_id,
|
||||||
is_primary: windows.is_primary(window_id),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput { ref input, .. } => {
|
WindowEvent::KeyboardInput { ref input, .. } => {
|
||||||
|
@ -165,7 +163,6 @@ fn handle_create_window_events(
|
||||||
windows.add(window);
|
windows.add(window);
|
||||||
window_created_events.send(WindowCreated {
|
window_created_events.send(WindowCreated {
|
||||||
id: window_id,
|
id: window_id,
|
||||||
is_primary: windows.is_primary(window_id),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,30 +87,64 @@ fn button_system(
|
||||||
|
|
||||||
fn setup(
|
fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
asset_server: Res<AssetServer>,
|
asset_server: Res<AssetServer>,
|
||||||
button_materials: Res<ButtonMaterials>,
|
button_materials: Res<ButtonMaterials>,
|
||||||
) {
|
) {
|
||||||
commands
|
commands
|
||||||
// ui camera
|
// ui camera
|
||||||
.spawn(Camera2dComponents::default())
|
.spawn(UiCameraComponents::default())
|
||||||
.spawn(ButtonComponents {
|
// wrapper component to center with flexbox
|
||||||
node: Node::new(Anchors::CENTER, Margins::new(-75.0, 75.0, -35.0, 35.0)),
|
.spawn(NodeComponents {
|
||||||
material: button_materials.normal,
|
flex: Flex {
|
||||||
|
size: Size {
|
||||||
|
width: Dimension::Percent(1.0),
|
||||||
|
height: Dimension::Percent(1.0),
|
||||||
|
},
|
||||||
|
align_items: AlignItems::Center,
|
||||||
|
justify_content: JustifyContent::Center,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
material: materials.add(Color::NONE.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
parent.spawn(TextComponents {
|
parent
|
||||||
node: Node::new(Anchors::FULL, Margins::new(0.0, 0.0, 12.0, 0.0)),
|
.spawn(ButtonComponents {
|
||||||
text: Text {
|
flex: Flex {
|
||||||
value: "Button".to_string(),
|
size: Size {
|
||||||
font: asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap(),
|
width: Dimension::Points(150.0),
|
||||||
style: TextStyle {
|
height: Dimension::Points(70.0),
|
||||||
font_size: 40.0,
|
},
|
||||||
color: Color::rgb(0.8, 0.8, 0.8),
|
..Default::default()
|
||||||
align: TextAlign::Center,
|
|
||||||
},
|
},
|
||||||
},
|
material: button_materials.normal,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
})
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent.spawn(TextComponents {
|
||||||
|
flex: Flex {
|
||||||
|
size: Size {
|
||||||
|
width: Dimension::Percent(1.0),
|
||||||
|
height: Dimension::Percent(1.0),
|
||||||
|
},
|
||||||
|
margin: Rect {
|
||||||
|
top: Dimension::Points(10.0),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
text: Text {
|
||||||
|
value: "Button".to_string(),
|
||||||
|
font: asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap(),
|
||||||
|
style: TextStyle {
|
||||||
|
font_size: 40.0,
|
||||||
|
color: Color::rgb(0.8, 0.8, 0.8),
|
||||||
|
align: TextAlign::Center,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
.spawn(Camera2dComponents::default())
|
.spawn(Camera2dComponents::default())
|
||||||
// texture
|
// texture
|
||||||
.spawn(TextComponents {
|
.spawn(TextComponents {
|
||||||
node: Node::new(Anchors::TOP_LEFT, Margins::new(0.0, 250.0, 0.0, 60.0)),
|
node: Node::default(),
|
||||||
text: Text {
|
text: Text {
|
||||||
value: "FPS:".to_string(),
|
value: "FPS:".to_string(),
|
||||||
font: font_handle,
|
font: font_handle,
|
||||||
|
|
|
@ -13,20 +13,26 @@ fn setup(
|
||||||
mut textures: ResMut<Assets<Texture>>,
|
mut textures: ResMut<Assets<Texture>>,
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
) {
|
) {
|
||||||
let texture_handle = asset_server
|
// let texture_handle = asset_server
|
||||||
.load_sync(&mut textures, "assets/branding/bevy_logo_dark_big.png")
|
// .load_sync(&mut textures, "assets/branding/bevy_logo_dark_big.png")
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
let font_handle = asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap();
|
// let texture = textures.get(&texture_handle).unwrap();
|
||||||
let texture = textures.get(&texture_handle).unwrap();
|
// let aspect = texture.aspect();
|
||||||
let aspect = texture.aspect();
|
|
||||||
|
|
||||||
commands
|
commands
|
||||||
// ui camera
|
// ui camera
|
||||||
.spawn(Camera2dComponents::default())
|
.spawn(UiCameraComponents::default())
|
||||||
// root node
|
// root node
|
||||||
.spawn(NodeComponents {
|
.spawn(NodeComponents {
|
||||||
node: Node::new(Anchors::FULL, Margins::default()),
|
flex: Flex {
|
||||||
|
size: Size {
|
||||||
|
width: Dimension::Percent(1.0),
|
||||||
|
height: Dimension::Percent(1.0),
|
||||||
|
},
|
||||||
|
justify_content: JustifyContent::SpaceBetween,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
material: materials.add(Color::NONE.into()),
|
material: materials.add(Color::NONE.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
|
@ -34,111 +40,205 @@ fn setup(
|
||||||
parent
|
parent
|
||||||
// left vertical fill
|
// left vertical fill
|
||||||
.spawn(NodeComponents {
|
.spawn(NodeComponents {
|
||||||
node: Node::new(Anchors::LEFT_FULL, Margins::new(10.0, 200.0, 10.0, 10.0)),
|
flex: Flex {
|
||||||
material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()),
|
size: Size {
|
||||||
|
width: Dimension::Points(200.0),
|
||||||
|
height: Dimension::Percent(1.0),
|
||||||
|
},
|
||||||
|
border: Rect {
|
||||||
|
start: Dimension::Points(10.0),
|
||||||
|
end: Dimension::Points(10.0),
|
||||||
|
top: Dimension::Points(10.0),
|
||||||
|
bottom: Dimension::Points(10.0),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
material: materials.add(Color::rgb(0.02, 0.02, 0.8).into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.with_children(|parent| {
|
.with_children(|parent| {
|
||||||
parent.spawn(TextComponents {
|
parent
|
||||||
node: Node::new(Anchors::TOP_LEFT, Margins::new(10.0, 200.0, 40.0, 10.0)),
|
.spawn(NodeComponents {
|
||||||
text: Text {
|
flex: Flex {
|
||||||
value: "Text Example".to_string(),
|
size: Size {
|
||||||
font: font_handle,
|
width: Dimension::Percent(1.0),
|
||||||
style: TextStyle {
|
height: Dimension::Percent(1.0),
|
||||||
font_size: 30.0,
|
},
|
||||||
color: Color::WHITE,
|
border: Rect {
|
||||||
|
bottom: Dimension::Points(5.0),
|
||||||
|
start: Dimension::Points(5.0),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
align_items: AlignItems::FlexEnd,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
},
|
material: materials.add(Color::rgb(0.8, 0.02, 0.02).into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
})
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent.spawn(TextComponents {
|
||||||
|
flex: Flex {
|
||||||
|
size: Size {
|
||||||
|
width: Dimension::Points(100.0),
|
||||||
|
height: Dimension::Points(30.0),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
text: Text {
|
||||||
|
value: "Text Example".to_string(),
|
||||||
|
font: asset_server
|
||||||
|
.load("assets/fonts/FiraSans-Bold.ttf")
|
||||||
|
.unwrap(),
|
||||||
|
style: TextStyle {
|
||||||
|
font_size: 30.0,
|
||||||
|
color: Color::WHITE,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
// right vertical fill
|
// right vertical fill
|
||||||
.spawn(NodeComponents {
|
.spawn(NodeComponents {
|
||||||
node: Node::new(Anchors::RIGHT_FULL, Margins::new(10.0, 100.0, 100.0, 100.0)),
|
flex: Flex {
|
||||||
|
size: Size {
|
||||||
|
width: Dimension::Points(100.0),
|
||||||
|
height: Dimension::Percent(1.0),
|
||||||
|
},
|
||||||
|
border: Rect {
|
||||||
|
start: Dimension::Points(10.0),
|
||||||
|
end: Dimension::Points(10.0),
|
||||||
|
top: Dimension::Points(10.0),
|
||||||
|
bottom: Dimension::Points(10.0),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()),
|
material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
|
||||||
// render order test: reddest in the back, whitest in the front
|
|
||||||
.spawn(NodeComponents {
|
|
||||||
node: Node::positioned(
|
|
||||||
Vec2::new(75.0, 60.0),
|
|
||||||
Anchors::CENTER,
|
|
||||||
Margins::new(0.0, 100.0, 0.0, 100.0),
|
|
||||||
),
|
|
||||||
material: materials.add(Color::rgb(1.0, 0.0, 0.0).into()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.spawn(NodeComponents {
|
|
||||||
node: Node::positioned(
|
|
||||||
Vec2::new(50.0, 35.0),
|
|
||||||
Anchors::CENTER,
|
|
||||||
Margins::new(0.0, 100.0, 0.0, 100.0),
|
|
||||||
),
|
|
||||||
material: materials.add(Color::rgb(1.0, 0.3, 0.3).into()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.spawn(NodeComponents {
|
|
||||||
node: Node::positioned(
|
|
||||||
Vec2::new(100.0, 85.0),
|
|
||||||
Anchors::CENTER,
|
|
||||||
Margins::new(0.0, 100.0, 0.0, 100.0),
|
|
||||||
),
|
|
||||||
material: materials.add(Color::rgb(1.0, 0.5, 0.5).into()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.spawn(NodeComponents {
|
|
||||||
node: Node::positioned(
|
|
||||||
Vec2::new(150.0, 135.0),
|
|
||||||
Anchors::CENTER,
|
|
||||||
Margins::new(0.0, 100.0, 0.0, 100.0),
|
|
||||||
),
|
|
||||||
material: materials.add(Color::rgb(1.0, 0.7, 0.7).into()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
// parenting
|
|
||||||
.spawn(NodeComponents {
|
|
||||||
node: Node::positioned(
|
|
||||||
Vec2::new(210.0, 0.0),
|
|
||||||
Anchors::BOTTOM_LEFT,
|
|
||||||
Margins::new(0.0, 200.0, 10.0, 210.0),
|
|
||||||
),
|
|
||||||
material: materials.add(Color::rgb(0.1, 0.1, 1.0).into()),
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
.with_children(|parent| {
|
|
||||||
parent.spawn(NodeComponents {
|
|
||||||
node: Node::new(Anchors::FULL, Margins::new(20.0, 20.0, 20.0, 20.0)),
|
|
||||||
material: materials.add(Color::rgb(0.6, 0.6, 1.0).into()),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
})
|
|
||||||
// alpha test
|
|
||||||
.spawn(NodeComponents {
|
|
||||||
node: Node::positioned(
|
|
||||||
Vec2::new(200.0, 185.0),
|
|
||||||
Anchors::CENTER,
|
|
||||||
Margins::new(0.0, 100.0, 0.0, 100.0),
|
|
||||||
),
|
|
||||||
material: materials.add(Color::rgba(1.0, 0.9, 0.9, 0.4).into()),
|
|
||||||
draw: Draw {
|
|
||||||
is_transparent: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
})
|
|
||||||
// texture
|
|
||||||
.spawn(NodeComponents {
|
|
||||||
node: Node::new(
|
|
||||||
Anchors::CENTER_TOP,
|
|
||||||
Margins::new(-250.0, 250.0, 510.0 * aspect, 10.0),
|
|
||||||
),
|
|
||||||
material: materials.add(ColorMaterial::texture(texture_handle)),
|
|
||||||
draw: Draw {
|
|
||||||
is_transparent: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
// // left vertical fill
|
||||||
|
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// flex: Flex {
|
||||||
|
// size: Size {
|
||||||
|
// width: Dimension::Percent(0.20),
|
||||||
|
// height: Dimension::Percent(0.20),
|
||||||
|
// },
|
||||||
|
// justify_content: JustifyContent::FlexEnd,
|
||||||
|
// align_items: AlignItems::FlexEnd,
|
||||||
|
// ..Default::default()
|
||||||
|
// },
|
||||||
|
// material: materials.add(Color::rgb(0.02, 0.8, 0.02).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// })
|
||||||
|
// .with_children(|parent| {
|
||||||
|
// parent.spawn(NodeComponents {
|
||||||
|
// flex: Flex {
|
||||||
|
// size: Size {
|
||||||
|
// width: Dimension::Percent(0.50),
|
||||||
|
// height: Dimension::Percent(0.50),
|
||||||
|
// },
|
||||||
|
// justify_content: JustifyContent::FlexEnd,
|
||||||
|
// align_items: AlignItems::FlexEnd,
|
||||||
|
// ..Default::default()
|
||||||
|
// },
|
||||||
|
// material: materials.add(Color::rgb(0.8, 0.02, 0.02).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// // right vertical fill
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// node: Node::new(Anchors::RIGHT_FULL, Margins::new(10.0, 100.0, 100.0, 100.0)),
|
||||||
|
// material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// })
|
||||||
|
// // render order test: reddest in the back, whitest in the front
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// node: Node::positioned(
|
||||||
|
// Vec2::new(75.0, 60.0),
|
||||||
|
// Anchors::CENTER,
|
||||||
|
// Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||||
|
// ),
|
||||||
|
// material: materials.add(Color::rgb(1.0, 0.0, 0.0).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// })
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// node: Node::positioned(
|
||||||
|
// Vec2::new(50.0, 35.0),
|
||||||
|
// Anchors::CENTER,
|
||||||
|
// Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||||
|
// ),
|
||||||
|
// material: materials.add(Color::rgb(1.0, 0.3, 0.3).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// })
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// node: Node::positioned(
|
||||||
|
// Vec2::new(100.0, 85.0),
|
||||||
|
// Anchors::CENTER,
|
||||||
|
// Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||||
|
// ),
|
||||||
|
// material: materials.add(Color::rgb(1.0, 0.5, 0.5).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// })
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// node: Node::positioned(
|
||||||
|
// Vec2::new(150.0, 135.0),
|
||||||
|
// Anchors::CENTER,
|
||||||
|
// Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||||
|
// ),
|
||||||
|
// material: materials.add(Color::rgb(1.0, 0.7, 0.7).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// })
|
||||||
|
// // parenting
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// node: Node::positioned(
|
||||||
|
// Vec2::new(210.0, 0.0),
|
||||||
|
// Anchors::BOTTOM_LEFT,
|
||||||
|
// Margins::new(0.0, 200.0, 10.0, 210.0),
|
||||||
|
// ),
|
||||||
|
// material: materials.add(Color::rgb(0.1, 0.1, 1.0).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// })
|
||||||
|
// .with_children(|parent| {
|
||||||
|
// parent.spawn(NodeComponents {
|
||||||
|
// node: Node::new(Anchors::FULL, Margins::new(20.0, 20.0, 20.0, 20.0)),
|
||||||
|
// material: materials.add(Color::rgb(0.6, 0.6, 1.0).into()),
|
||||||
|
// ..Default::default()
|
||||||
|
// });
|
||||||
|
// })
|
||||||
|
// // alpha test
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// node: Node::positioned(
|
||||||
|
// Vec2::new(200.0, 185.0),
|
||||||
|
// Anchors::CENTER,
|
||||||
|
// Margins::new(0.0, 100.0, 0.0, 100.0),
|
||||||
|
// ),
|
||||||
|
// material: materials.add(Color::rgba(1.0, 0.9, 0.9, 0.4).into()),
|
||||||
|
// draw: Draw {
|
||||||
|
// is_transparent: true,
|
||||||
|
// ..Default::default()
|
||||||
|
// },
|
||||||
|
// ..Default::default()
|
||||||
|
// })
|
||||||
|
// // texture
|
||||||
|
// .spawn(NodeComponents {
|
||||||
|
// node: Node::new(
|
||||||
|
// Anchors::CENTER_TOP,
|
||||||
|
// Margins::new(-250.0, 250.0, 510.0 * aspect, 10.0),
|
||||||
|
// ),
|
||||||
|
// material: materials.add(ColorMaterial::texture(texture_handle)),
|
||||||
|
// draw: Draw {
|
||||||
|
// is_transparent: true,
|
||||||
|
// ..Default::default()
|
||||||
|
// },
|
||||||
|
// ..Default::default()
|
||||||
|
// });
|
||||||
|
})
|
||||||
|
.spawn(NodeComponents {
|
||||||
|
material: materials.add(Color::rgb(1.0, 0.0, 0.0).into()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue