2024-08-06 10:54:37 +00:00
|
|
|
//! Displays information about available monitors (displays).
|
|
|
|
|
2024-09-24 11:42:59 +00:00
|
|
|
use bevy::{
|
|
|
|
prelude::*,
|
|
|
|
render::camera::RenderTarget,
|
|
|
|
window::{ExitCondition, Monitor, WindowMode, WindowRef},
|
|
|
|
};
|
2024-08-06 10:54:37 +00:00
|
|
|
|
|
|
|
fn main() {
|
|
|
|
App::new()
|
|
|
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
|
|
|
primary_window: None,
|
|
|
|
exit_condition: ExitCondition::DontExit,
|
|
|
|
..default()
|
|
|
|
}))
|
|
|
|
.add_systems(Update, (update, close_on_esc))
|
|
|
|
.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
struct MonitorRef(Entity);
|
|
|
|
|
|
|
|
fn update(
|
|
|
|
mut commands: Commands,
|
|
|
|
monitors_added: Query<(Entity, &Monitor), Added<Monitor>>,
|
|
|
|
mut monitors_removed: RemovedComponents<Monitor>,
|
|
|
|
monitor_refs: Query<(Entity, &MonitorRef)>,
|
|
|
|
) {
|
|
|
|
for (entity, monitor) in monitors_added.iter() {
|
|
|
|
// Spawn a new window on each monitor
|
|
|
|
let name = monitor.name.clone().unwrap_or_else(|| "<no name>".into());
|
|
|
|
let size = format!("{}x{}px", monitor.physical_height, monitor.physical_width);
|
|
|
|
let hz = monitor
|
|
|
|
.refresh_rate_millihertz
|
|
|
|
.map(|x| format!("{}Hz", x as f32 / 1000.0))
|
|
|
|
.unwrap_or_else(|| "<unknown>".into());
|
|
|
|
let position = format!(
|
|
|
|
"x={} y={}",
|
|
|
|
monitor.physical_position.x, monitor.physical_position.y
|
|
|
|
);
|
|
|
|
let scale = format!("{:.2}", monitor.scale_factor);
|
|
|
|
|
|
|
|
let window = commands
|
|
|
|
.spawn((
|
|
|
|
Window {
|
|
|
|
title: name.clone(),
|
|
|
|
mode: WindowMode::Fullscreen(MonitorSelection::Entity(entity)),
|
|
|
|
position: WindowPosition::Centered(MonitorSelection::Entity(entity)),
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
MonitorRef(entity),
|
|
|
|
))
|
|
|
|
.id();
|
|
|
|
|
|
|
|
let camera = commands
|
2024-10-05 01:59:52 +00:00
|
|
|
.spawn((
|
|
|
|
Camera2d,
|
|
|
|
Camera {
|
2024-08-06 10:54:37 +00:00
|
|
|
target: RenderTarget::Window(WindowRef::Entity(window)),
|
|
|
|
..default()
|
|
|
|
},
|
2024-10-05 01:59:52 +00:00
|
|
|
))
|
2024-08-06 10:54:37 +00:00
|
|
|
.id();
|
|
|
|
|
|
|
|
let info_text = format!(
|
|
|
|
"Monitor: {name}\nSize: {size}\nRefresh rate: {hz}\nPosition: {position}\nScale: {scale}\n\n",
|
|
|
|
);
|
|
|
|
commands.spawn((
|
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.**
# Objective
- Implement https://github.com/bevyengine/bevy/discussions/15014
## Solution
This implements [cart's
proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459)
faithfully except for one change. I separated `TextSpan` from
`TextSpan2d` because `TextSpan` needs to require the `GhostNode`
component, which is a `bevy_ui` component only usable by UI.
Extra changes:
- Added `EntityCommands::commands_mut` that returns a mutable reference.
This is a blocker for extension methods that return something other than
`self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable
reference for this reason.
## Testing
- [x] Text examples all work.
---
## Showcase
TODO: showcase-worthy
## Migration Guide
TODO: very breaking
### Accessing text spans by index
Text sections are now text sections on different entities in a
hierarchy, Use the new `TextReader` and `TextWriter` system parameters
to access spans by index.
Before:
```rust
fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
let text = query.single_mut();
text.sections[1].value = format_time(time.elapsed());
}
```
After:
```rust
fn refresh_text(
query: Query<Entity, With<TimeText>>,
mut writer: UiTextWriter,
time: Res<Time>
) {
let entity = query.single();
*writer.text(entity, 1) = format_time(time.elapsed());
}
```
### Iterating text spans
Text spans are now entities in a hierarchy, so the new `UiTextReader`
and `UiTextWriter` system parameters provide ways to iterate that
hierarchy. The `UiTextReader::iter` method will give you a normal
iterator over spans, and `UiTextWriter::for_each` lets you visit each of
the spans.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-09 18:35:36 +00:00
|
|
|
Text(info_text),
|
Merge Style properties into Node. Use ComputedNode for computed properties. (#15975)
# Objective
Continue improving the user experience of our UI Node API in the
direction specified by [Bevy's Next Generation Scene / UI
System](https://github.com/bevyengine/bevy/discussions/14437)
## Solution
As specified in the document above, merge `Style` fields into `Node`,
and move "computed Node fields" into `ComputedNode` (I chose this name
over something like `ComputedNodeLayout` because it currently contains
more than just layout info. If we want to break this up / rename these
concepts, lets do that in a separate PR). `Style` has been removed.
This accomplishes a number of goals:
## Ergonomics wins
Specifying both `Node` and `Style` is now no longer required for
non-default styles
Before:
```rust
commands.spawn((
Node::default(),
Style {
width: Val::Px(100.),
..default()
},
));
```
After:
```rust
commands.spawn(Node {
width: Val::Px(100.),
..default()
});
```
## Conceptual clarity
`Style` was never a comprehensive "style sheet". It only defined "core"
style properties that all `Nodes` shared. Any "styled property" that
couldn't fit that mold had to be in a separate component. A "real" style
system would style properties _across_ components (`Node`, `Button`,
etc). We have plans to build a true style system (see the doc linked
above).
By moving the `Style` fields to `Node`, we fully embrace `Node` as the
driving concept and remove the "style system" confusion.
## Next Steps
* Consider identifying and splitting out "style properties that aren't
core to Node". This should not happen for Bevy 0.15.
---
## Migration Guide
Move any fields set on `Style` into `Node` and replace all `Style`
component usage with `Node`.
Before:
```rust
commands.spawn((
Node::default(),
Style {
width: Val::Px(100.),
..default()
},
));
```
After:
```rust
commands.spawn(Node {
width: Val::Px(100.),
..default()
});
```
For any usage of the "computed node properties" that used to live on
`Node`, use `ComputedNode` instead:
Before:
```rust
fn system(nodes: Query<&Node>) {
for node in &nodes {
let computed_size = node.size();
}
}
```
After:
```rust
fn system(computed_nodes: Query<&ComputedNode>) {
for computed_node in &computed_nodes {
let computed_size = computed_node.size();
}
}
```
2024-10-18 22:25:33 +00:00
|
|
|
Node {
|
2024-08-06 10:54:37 +00:00
|
|
|
position_type: PositionType::Relative,
|
|
|
|
height: Val::Percent(100.0),
|
|
|
|
width: Val::Percent(100.0),
|
|
|
|
..default()
|
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.**
# Objective
- Implement https://github.com/bevyengine/bevy/discussions/15014
## Solution
This implements [cart's
proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459)
faithfully except for one change. I separated `TextSpan` from
`TextSpan2d` because `TextSpan` needs to require the `GhostNode`
component, which is a `bevy_ui` component only usable by UI.
Extra changes:
- Added `EntityCommands::commands_mut` that returns a mutable reference.
This is a blocker for extension methods that return something other than
`self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable
reference for this reason.
## Testing
- [x] Text examples all work.
---
## Showcase
TODO: showcase-worthy
## Migration Guide
TODO: very breaking
### Accessing text spans by index
Text sections are now text sections on different entities in a
hierarchy, Use the new `TextReader` and `TextWriter` system parameters
to access spans by index.
Before:
```rust
fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
let text = query.single_mut();
text.sections[1].value = format_time(time.elapsed());
}
```
After:
```rust
fn refresh_text(
query: Query<Entity, With<TimeText>>,
mut writer: UiTextWriter,
time: Res<Time>
) {
let entity = query.single();
*writer.text(entity, 1) = format_time(time.elapsed());
}
```
### Iterating text spans
Text spans are now entities in a hierarchy, so the new `UiTextReader`
and `UiTextWriter` system parameters provide ways to iterate that
hierarchy. The `UiTextReader::iter` method will give you a normal
iterator over spans, and `UiTextWriter::for_each` lets you visit each of
the spans.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-09 18:35:36 +00:00
|
|
|
},
|
2024-08-06 10:54:37 +00:00
|
|
|
TargetCamera(camera),
|
|
|
|
MonitorRef(entity),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove windows for removed monitors
|
|
|
|
for monitor_entity in monitors_removed.read() {
|
|
|
|
for (ref_entity, monitor_ref) in monitor_refs.iter() {
|
|
|
|
if monitor_ref.0 == monitor_entity {
|
|
|
|
commands.entity(ref_entity).despawn_recursive();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn close_on_esc(
|
|
|
|
mut commands: Commands,
|
|
|
|
focused_windows: Query<(Entity, &Window)>,
|
|
|
|
input: Res<ButtonInput<KeyCode>>,
|
|
|
|
) {
|
|
|
|
for (window, focus) in focused_windows.iter() {
|
|
|
|
if !focus.focused {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if input.just_pressed(KeyCode::Escape) {
|
|
|
|
commands.entity(window).despawn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|