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(),
|
||||
executor: Default::default(),
|
||||
startup_schedule: Default::default(),
|
||||
startup_executor: Default::default(),
|
||||
startup_executor: ParallelExecutor::without_tracker_clears(),
|
||||
runner: Box::new(run_once),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use std::sync::{Arc, Mutex};
|
|||
pub struct ParallelExecutor {
|
||||
stages: Vec<ExecutorStage>,
|
||||
last_schedule_generation: usize,
|
||||
clear_trackers: bool,
|
||||
}
|
||||
|
||||
impl Default for ParallelExecutor {
|
||||
|
@ -19,11 +20,18 @@ impl Default for ParallelExecutor {
|
|||
Self {
|
||||
stages: Default::default(),
|
||||
last_schedule_generation: usize::MAX, // MAX forces prepare to run the first time
|
||||
clear_trackers: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParallelExecutor {
|
||||
pub fn without_tracker_clears() -> Self {
|
||||
Self {
|
||||
clear_trackers: false,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
pub fn prepare(&mut self, schedule: &mut Schedule, world: &World) {
|
||||
let schedule_generation = 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 bevy_app::prelude::{EventReader, Events};
|
||||
use bevy_ecs::{Component, Local, Query, Res};
|
||||
use bevy_math::Mat4;
|
||||
use bevy_property::Properties;
|
||||
use bevy_window::{WindowCreated, WindowReference, WindowResized, Windows};
|
||||
use bevy_app::prelude::{Events, EventReader};
|
||||
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
|
||||
|
||||
#[derive(Default, Debug, Properties)]
|
||||
pub struct Camera {
|
||||
pub projection_matrix: Mat4,
|
||||
pub name: Option<String>,
|
||||
#[property(ignore)]
|
||||
pub window: WindowReference,
|
||||
pub window: WindowId,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -27,7 +27,6 @@ pub fn camera_system<T: CameraProjection + Component>(
|
|||
mut query: Query<(&mut Camera, &mut T)>,
|
||||
) {
|
||||
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
|
||||
for event in state
|
||||
.window_resized_event_reader
|
||||
|
@ -38,11 +37,7 @@ pub fn camera_system<T: CameraProjection + Component>(
|
|||
continue;
|
||||
}
|
||||
|
||||
if event.is_primary {
|
||||
changed_primary_window_id = Some(event.id);
|
||||
} else {
|
||||
changed_window_ids.push(event.id);
|
||||
}
|
||||
changed_window_ids.push(event.id);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if event.is_primary {
|
||||
changed_primary_window_id = Some(event.id);
|
||||
} else {
|
||||
changed_window_ids.push(event.id);
|
||||
}
|
||||
changed_window_ids.push(event.id);
|
||||
}
|
||||
|
||||
for (mut camera, mut camera_projection) in &mut query.iter() {
|
||||
if let Some(window) = match 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
|
||||
}
|
||||
}
|
||||
} {
|
||||
if let Some(window) = windows.get(camera.window) {
|
||||
camera_projection.update(window.width as usize, window.height as usize);
|
||||
camera.projection_matrix = camera_projection.get_projection_matrix();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
texture::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsage},
|
||||
Color,
|
||||
};
|
||||
use bevy_window::WindowReference;
|
||||
use bevy_window::WindowId;
|
||||
|
||||
pub struct BaseRenderGraphConfig {
|
||||
pub add_2d_camera: bool,
|
||||
|
@ -71,7 +71,7 @@ impl BaseRenderGraphBuilder for RenderGraph {
|
|||
self.add_node(
|
||||
node::MAIN_DEPTH_TEXTURE,
|
||||
WindowTextureNode::new(
|
||||
WindowReference::Primary,
|
||||
WindowId::primary(),
|
||||
TextureDescriptor {
|
||||
size: Extent3d {
|
||||
depth: 1,
|
||||
|
@ -137,7 +137,7 @@ impl BaseRenderGraphBuilder for RenderGraph {
|
|||
|
||||
self.add_node(
|
||||
node::PRIMARY_SWAP_CHAIN,
|
||||
WindowSwapChainNode::new(WindowReference::Primary),
|
||||
WindowSwapChainNode::new(WindowId::primary()),
|
||||
);
|
||||
|
||||
if config.connect_main_pass_to_swapchain {
|
||||
|
|
|
@ -4,20 +4,20 @@ use crate::{
|
|||
};
|
||||
use bevy_app::prelude::{EventReader, Events};
|
||||
use bevy_ecs::{Resources, World};
|
||||
use bevy_window::{WindowCreated, WindowReference, WindowResized, Windows};
|
||||
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub struct WindowSwapChainNode {
|
||||
window_reference: WindowReference,
|
||||
window_id: WindowId,
|
||||
window_created_event_reader: EventReader<WindowCreated>,
|
||||
window_resized_event_reader: EventReader<WindowResized>,
|
||||
}
|
||||
|
||||
impl WindowSwapChainNode {
|
||||
pub const OUT_TEXTURE: &'static str = "texture";
|
||||
pub fn new(window_reference: WindowReference) -> Self {
|
||||
pub fn new(window_id: WindowId) -> Self {
|
||||
WindowSwapChainNode {
|
||||
window_reference,
|
||||
window_id,
|
||||
window_created_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 windows = resources.get::<Windows>().unwrap();
|
||||
|
||||
let window = match self.window_reference {
|
||||
WindowReference::Primary => windows.get_primary().expect("No primary window exists"),
|
||||
WindowReference::Id(id) => windows
|
||||
.get(id)
|
||||
.expect("Received window resized event for non-existent window"),
|
||||
};
|
||||
let window = windows
|
||||
.get(self.window_id)
|
||||
.expect("Received window resized event for non-existent window");
|
||||
|
||||
let render_resource_context = render_context.resources_mut();
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ use crate::{
|
|||
};
|
||||
use bevy_app::prelude::{EventReader, Events};
|
||||
use bevy_ecs::{Resources, World};
|
||||
use bevy_window::{WindowCreated, WindowReference, WindowResized, Windows};
|
||||
use bevy_window::{WindowCreated, WindowId, WindowResized, Windows};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub struct WindowTextureNode {
|
||||
window_reference: WindowReference,
|
||||
window_id: WindowId,
|
||||
descriptor: TextureDescriptor,
|
||||
window_created_event_reader: EventReader<WindowCreated>,
|
||||
window_resized_event_reader: EventReader<WindowResized>,
|
||||
|
@ -17,9 +17,9 @@ pub struct WindowTextureNode {
|
|||
|
||||
impl WindowTextureNode {
|
||||
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 {
|
||||
window_reference,
|
||||
window_id,
|
||||
descriptor,
|
||||
window_created_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 windows = resources.get::<Windows>().unwrap();
|
||||
|
||||
let window = match self.window_reference {
|
||||
WindowReference::Primary => windows.get_primary().expect("No primary window exists"),
|
||||
WindowReference::Id(id) => windows
|
||||
.get(id)
|
||||
.expect("Received window resized event for non-existent window"),
|
||||
};
|
||||
let window = windows
|
||||
.get(self.window_id)
|
||||
.expect("Received window resized event for non-existent window");
|
||||
|
||||
if self
|
||||
.window_created_event_reader
|
||||
|
|
|
@ -5,9 +5,9 @@ pub fn run_on_hierarchy<T, S>(
|
|||
children_query: &Query<&Children>,
|
||||
state: &mut S,
|
||||
entity: Entity,
|
||||
parent_result: Option<&mut T>,
|
||||
parent_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>
|
||||
where
|
||||
T: Clone,
|
||||
|
@ -25,7 +25,7 @@ where
|
|||
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;
|
||||
if let Some(children) = children {
|
||||
for child in children {
|
||||
|
@ -33,7 +33,7 @@ where
|
|||
children_query,
|
||||
state,
|
||||
child,
|
||||
parent_result.as_mut(),
|
||||
parent_result.clone(),
|
||||
previous_result,
|
||||
run,
|
||||
);
|
||||
|
|
|
@ -18,4 +18,6 @@ bevy_transform = { path = "../bevy_transform" }
|
|||
bevy_render = { path = "../bevy_render" }
|
||||
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 crate::{
|
||||
prelude::Flex,
|
||||
render::UI_PIPELINE_HANDLE,
|
||||
widget::{Button, Text},
|
||||
Click, FocusPolicy, Hover,
|
||||
Click, FlexSurfaceId, FocusPolicy, Hover,
|
||||
};
|
||||
use bevy_asset::Handle;
|
||||
use bevy_ecs::Bundle;
|
||||
use bevy_render::{
|
||||
camera::{Camera, OrthographicProjection, VisibleEntities, WindowOrigin},
|
||||
draw::Draw,
|
||||
mesh::Mesh,
|
||||
pipeline::{DynamicBinding, PipelineSpecialization, RenderPipeline, RenderPipelines},
|
||||
};
|
||||
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)]
|
||||
pub struct NodeComponents {
|
||||
pub node: Node,
|
||||
pub flex: Flex,
|
||||
pub flex_surface_id: FlexSurfaceId,
|
||||
pub mesh: Handle<Mesh>, // TODO: maybe abstract this out
|
||||
pub material: Handle<ColorMaterial>,
|
||||
pub draw: Draw,
|
||||
|
@ -48,6 +55,8 @@ impl Default for NodeComponents {
|
|||
},
|
||||
)]),
|
||||
node: Default::default(),
|
||||
flex_surface_id: Default::default(),
|
||||
flex: Flex::default(),
|
||||
material: Default::default(),
|
||||
draw: Default::default(),
|
||||
transform: Default::default(),
|
||||
|
@ -59,6 +68,8 @@ impl Default for NodeComponents {
|
|||
#[derive(Bundle)]
|
||||
pub struct TextComponents {
|
||||
pub node: Node,
|
||||
pub flex: Flex,
|
||||
pub flex_surface_id: FlexSurfaceId,
|
||||
pub draw: Draw,
|
||||
pub text: Text,
|
||||
pub focus_policy: FocusPolicy,
|
||||
|
@ -71,6 +82,8 @@ impl Default for TextComponents {
|
|||
TextComponents {
|
||||
text: Text::default(),
|
||||
node: Default::default(),
|
||||
flex: Flex::default(),
|
||||
flex_surface_id: Default::default(),
|
||||
focus_policy: FocusPolicy::Pass,
|
||||
draw: Draw {
|
||||
is_transparent: true,
|
||||
|
@ -86,6 +99,8 @@ impl Default for TextComponents {
|
|||
pub struct ButtonComponents {
|
||||
pub node: Node,
|
||||
pub button: Button,
|
||||
pub flex: Flex,
|
||||
pub flex_surface_id: FlexSurfaceId,
|
||||
pub click: Click,
|
||||
pub hover: Hover,
|
||||
pub focus_policy: FocusPolicy,
|
||||
|
@ -124,6 +139,8 @@ impl Default for ButtonComponents {
|
|||
},
|
||||
)]),
|
||||
node: Default::default(),
|
||||
flex_surface_id: Default::default(),
|
||||
flex: Flex::default(),
|
||||
material: Default::default(),
|
||||
draw: 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(
|
||||
mut state: Local<State>,
|
||||
windows: Res<Windows>,
|
||||
mouse_button_input: Res<Input<MouseButton>>,
|
||||
cursor_moved_events: Res<Events<CursorMoved>>,
|
||||
mut node_query: Query<(
|
||||
|
@ -79,7 +78,6 @@ pub fn ui_focus_system(
|
|||
}
|
||||
|
||||
let mouse_clicked = mouse_button_input.just_pressed(MouseButton::Left);
|
||||
let window = windows.get_primary().unwrap();
|
||||
let mut hovered_entity = None;
|
||||
|
||||
{
|
||||
|
@ -88,9 +86,7 @@ pub fn ui_focus_system(
|
|||
.iter()
|
||||
.filter_map(|(entity, node, transform, click, hover, focus_policy)| {
|
||||
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()
|
||||
+ Vec2::new(window.width as f32 / 2.0, window.height as f32 / 2.0);
|
||||
let ui_position = position.truncate().truncate();
|
||||
let extents = node.size / 2.0;
|
||||
let min = ui_position - extents;
|
||||
let max = ui_position + extents;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
mod anchors;
|
||||
mod focus;
|
||||
pub mod entity;
|
||||
mod flex;
|
||||
mod focus;
|
||||
mod margins;
|
||||
mod node;
|
||||
mod render;
|
||||
|
@ -8,6 +9,7 @@ pub mod update;
|
|||
pub mod widget;
|
||||
|
||||
pub use anchors::*;
|
||||
pub use flex::*;
|
||||
pub use focus::*;
|
||||
pub use margins::*;
|
||||
pub use node::*;
|
||||
|
@ -19,21 +21,32 @@ pub mod prelude {
|
|||
widget::{Button, Text},
|
||||
Anchors, Click, Hover, Margins, Node,
|
||||
};
|
||||
|
||||
pub use stretch::{
|
||||
geometry::{Point, Rect, Size},
|
||||
style::{Style as Flex, *},
|
||||
};
|
||||
}
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::IntoQuerySystem;
|
||||
use bevy_render::render_graph::RenderGraph;
|
||||
use update::ui_update_system;
|
||||
use update::ui_z_system;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct UiPlugin;
|
||||
|
||||
impl AppPlugin for UiPlugin {
|
||||
fn build(&self, app: &mut AppBuilder) {
|
||||
app.add_system_to_stage(stage::PRE_UPDATE, ui_focus_system.system())
|
||||
// must run before transform update systems
|
||||
.add_system_to_stage_front(stage::POST_UPDATE, ui_update_system.system())
|
||||
app.init_resource::<FlexSurfaces>()
|
||||
.add_system_to_stage(stage::PRE_UPDATE, ui_focus_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(bevy_render::stage::DRAW, widget::draw_text_system.system());
|
||||
|
||||
|
|
|
@ -1,108 +1,7 @@
|
|||
use super::{Anchors, Margins};
|
||||
use bevy_math::{Mat4, Vec2, Vec3};
|
||||
use bevy_math::Vec2;
|
||||
use bevy_render::renderer::RenderResources;
|
||||
use bevy_transform::components::LocalTransform;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum MarginGrowDirection {
|
||||
Negative,
|
||||
Positive,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, RenderResources)]
|
||||
pub struct Node {
|
||||
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 bevy_ecs::{Entity, Query, Res, Without};
|
||||
use bevy_math::Vec2;
|
||||
use crate::FlexSurfaceId;
|
||||
use bevy_ecs::{Entity, Query, With, Without};
|
||||
use bevy_transform::{
|
||||
hierarchy,
|
||||
prelude::{Children, LocalTransform, Parent},
|
||||
};
|
||||
use bevy_window::Windows;
|
||||
|
||||
pub const UI_Z_STEP: f32 = 0.001;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Rect {
|
||||
pub z: f32,
|
||||
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)>,
|
||||
pub fn ui_z_system(
|
||||
mut root_node_query: Query<With<Node, Without<Parent, (Entity, &FlexSurfaceId)>>>,
|
||||
mut node_query: Query<(Entity, &Node, &mut FlexSurfaceId, &mut LocalTransform)>,
|
||||
children_query: Query<&Children>,
|
||||
) {
|
||||
let window_size = if let Some(window) = windows.get_primary() {
|
||||
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 window_z = 0.0;
|
||||
|
||||
let mut previous_sibling_result = Some(Rect {
|
||||
z: 0.0,
|
||||
size: window_size,
|
||||
});
|
||||
for entity in orphan_nodes {
|
||||
previous_sibling_result = hierarchy::run_on_hierarchy(
|
||||
// PERF: we can probably avoid an allocation here by making root_node_query and node_query non-overlapping
|
||||
let root_nodes = (&mut root_node_query.iter())
|
||||
.iter()
|
||||
.map(|(e, s)| (e, *s))
|
||||
.collect::<Vec<(Entity, FlexSurfaceId)>>();
|
||||
|
||||
for (entity, flex_surface_id) in root_nodes {
|
||||
if let Some(result) = hierarchy::run_on_hierarchy(
|
||||
&children_query,
|
||||
&mut node_query,
|
||||
entity,
|
||||
Some(&mut window_rect),
|
||||
previous_sibling_result,
|
||||
Some((flex_surface_id, window_z)),
|
||||
Some((flex_surface_id, window_z)),
|
||||
&mut update_node_entity,
|
||||
);
|
||||
) {
|
||||
window_z = result.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
parent_rect: Option<&mut Rect>,
|
||||
previous_rect: Option<Rect>,
|
||||
) -> Option<Rect> {
|
||||
if let Ok(mut node) = node_query.get_mut::<Node>(entity) {
|
||||
if let Ok(mut local_transform) = node_query.get_mut::<LocalTransform>(entity) {
|
||||
let parent_rect = parent_rect.unwrap();
|
||||
let mut z = UI_Z_STEP;
|
||||
if let Some(previous_rect) = previous_rect {
|
||||
z += previous_rect.z
|
||||
};
|
||||
parent_result: Option<(FlexSurfaceId, f32)>,
|
||||
previous_result: Option<(FlexSurfaceId, f32)>,
|
||||
) -> Option<(FlexSurfaceId, f32)> {
|
||||
let mut surface_id = node_query.get_mut::<FlexSurfaceId>(entity).unwrap();
|
||||
let mut transform = node_query.get_mut::<LocalTransform>(entity).unwrap();
|
||||
let (parent_surface_id, _) = parent_result?;
|
||||
let mut z = UI_Z_STEP;
|
||||
if let Some((_, previous_z)) = previous_result {
|
||||
z += previous_z;
|
||||
};
|
||||
|
||||
node.update(&mut local_transform, z, parent_rect.size);
|
||||
return Some(Rect { size: node.size, z });
|
||||
}
|
||||
}
|
||||
let mut position = transform.w_axis();
|
||||
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 width: usize,
|
||||
pub height: usize,
|
||||
pub is_primary: bool,
|
||||
}
|
||||
|
||||
/// An event that indicates that a new window should be created.
|
||||
|
@ -27,7 +26,6 @@ pub struct CloseWindow {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct WindowCreated {
|
||||
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
|
||||
|
@ -35,7 +33,6 @@ pub struct WindowCreated {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct WindowCloseRequested {
|
||||
pub id: WindowId,
|
||||
pub is_primary: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -47,7 +47,7 @@ impl AppPlugin for WindowPlugin {
|
|||
.unwrap_or_else(|| WindowDescriptor::default());
|
||||
let mut create_window_event = resources.get_mut::<Events<CreateWindow>>().unwrap();
|
||||
create_window_event.send(CreateWindow {
|
||||
id: WindowId::new(),
|
||||
id: WindowId::primary(),
|
||||
descriptor: window_descriptor.clone(),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,17 +1,5 @@
|
|||
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)]
|
||||
pub struct WindowId(Uuid);
|
||||
|
||||
|
@ -20,11 +8,25 @@ impl WindowId {
|
|||
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 {
|
||||
self.0.to_simple().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WindowId {
|
||||
fn default() -> Self {
|
||||
WindowId::primary()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Window {
|
||||
pub id: WindowId,
|
||||
|
|
|
@ -4,15 +4,10 @@ use std::collections::HashMap;
|
|||
#[derive(Default)]
|
||||
pub struct Windows {
|
||||
windows: HashMap<WindowId, Window>,
|
||||
primary_window: Option<WindowId>,
|
||||
}
|
||||
|
||||
impl Windows {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -25,14 +20,7 @@ impl Windows {
|
|||
}
|
||||
|
||||
pub fn get_primary(&self) -> Option<&Window> {
|
||||
self.primary_window
|
||||
.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)
|
||||
self.get(WindowId::primary())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &Window> {
|
||||
|
|
|
@ -76,7 +76,6 @@ pub fn winit_runner(mut app: App) {
|
|||
id: window_id,
|
||||
height: window.height as usize,
|
||||
width: window.width as usize,
|
||||
is_primary: windows.is_primary(window_id),
|
||||
});
|
||||
}
|
||||
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();
|
||||
window_close_requested_events.send(WindowCloseRequested {
|
||||
id: window_id,
|
||||
is_primary: windows.is_primary(window_id),
|
||||
});
|
||||
}
|
||||
WindowEvent::KeyboardInput { ref input, .. } => {
|
||||
|
@ -165,7 +163,6 @@ fn handle_create_window_events(
|
|||
windows.add(window);
|
||||
window_created_events.send(WindowCreated {
|
||||
id: window_id,
|
||||
is_primary: windows.is_primary(window_id),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,30 +87,64 @@ fn button_system(
|
|||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
button_materials: Res<ButtonMaterials>,
|
||||
) {
|
||||
commands
|
||||
// ui camera
|
||||
.spawn(Camera2dComponents::default())
|
||||
.spawn(ButtonComponents {
|
||||
node: Node::new(Anchors::CENTER, Margins::new(-75.0, 75.0, -35.0, 35.0)),
|
||||
material: button_materials.normal,
|
||||
.spawn(UiCameraComponents::default())
|
||||
// wrapper component to center with flexbox
|
||||
.spawn(NodeComponents {
|
||||
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()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn(TextComponents {
|
||||
node: Node::new(Anchors::FULL, Margins::new(0.0, 0.0, 12.0, 0.0)),
|
||||
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,
|
||||
parent
|
||||
.spawn(ButtonComponents {
|
||||
flex: Flex {
|
||||
size: Size {
|
||||
width: Dimension::Points(150.0),
|
||||
height: Dimension::Points(70.0),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
material: button_materials.normal,
|
||||
..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())
|
||||
// texture
|
||||
.spawn(TextComponents {
|
||||
node: Node::new(Anchors::TOP_LEFT, Margins::new(0.0, 250.0, 0.0, 60.0)),
|
||||
node: Node::default(),
|
||||
text: Text {
|
||||
value: "FPS:".to_string(),
|
||||
font: font_handle,
|
||||
|
|
|
@ -13,20 +13,26 @@ fn setup(
|
|||
mut textures: ResMut<Assets<Texture>>,
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
) {
|
||||
let texture_handle = asset_server
|
||||
.load_sync(&mut textures, "assets/branding/bevy_logo_dark_big.png")
|
||||
.unwrap();
|
||||
// let texture_handle = asset_server
|
||||
// .load_sync(&mut textures, "assets/branding/bevy_logo_dark_big.png")
|
||||
// .unwrap();
|
||||
|
||||
let font_handle = asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap();
|
||||
let texture = textures.get(&texture_handle).unwrap();
|
||||
let aspect = texture.aspect();
|
||||
// let texture = textures.get(&texture_handle).unwrap();
|
||||
// let aspect = texture.aspect();
|
||||
|
||||
commands
|
||||
// ui camera
|
||||
.spawn(Camera2dComponents::default())
|
||||
.spawn(UiCameraComponents::default())
|
||||
// root node
|
||||
.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()),
|
||||
..Default::default()
|
||||
})
|
||||
|
@ -34,111 +40,205 @@ fn setup(
|
|||
parent
|
||||
// left vertical fill
|
||||
.spawn(NodeComponents {
|
||||
node: Node::new(Anchors::LEFT_FULL, Margins::new(10.0, 200.0, 10.0, 10.0)),
|
||||
material: materials.add(Color::rgb(0.02, 0.02, 0.02).into()),
|
||||
flex: Flex {
|
||||
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()
|
||||
})
|
||||
.with_children(|parent| {
|
||||
parent.spawn(TextComponents {
|
||||
node: Node::new(Anchors::TOP_LEFT, Margins::new(10.0, 200.0, 40.0, 10.0)),
|
||||
text: Text {
|
||||
value: "Text Example".to_string(),
|
||||
font: font_handle,
|
||||
style: TextStyle {
|
||||
font_size: 30.0,
|
||||
color: Color::WHITE,
|
||||
parent
|
||||
.spawn(NodeComponents {
|
||||
flex: Flex {
|
||||
size: Size {
|
||||
width: Dimension::Percent(1.0),
|
||||
height: Dimension::Percent(1.0),
|
||||
},
|
||||
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()
|
||||
})
|
||||
.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
|
||||
.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()),
|
||||
..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