use bevy::prelude::*;
use bevy::ui::UiTargetCamera;
use bevy_kana::ScreenPosition;
use super::constants::LABEL_FONT_SIZE;
use super::constants::LABEL_PIXEL_OFFSET;
use super::screen_space;
use crate::fit::Edge;
use crate::support::ScreenSpaceBounds;
#[derive(Component, Reflect)]
#[reflect(Component)]
pub(super) struct MarginLabel {
pub(super) edge: Edge,
pub(super) camera: Entity,
}
pub(super) struct MarginLabelParams {
pub(super) camera: Entity,
pub(super) edge: Edge,
pub(super) text: String,
pub(super) color: Color,
pub(super) screen_pos: ScreenPosition,
pub(super) viewport_size: Vec2,
}
#[derive(Component, Reflect)]
#[reflect(Component)]
pub(super) struct BoundsLabel {
pub(super) camera: Entity,
}
pub(super) fn calculate_label_pixel_position(
edge: Edge,
bounds: &ScreenSpaceBounds,
viewport_size: Vec2,
) -> ScreenPosition {
let (screen_x, screen_y) = screen_space::screen_edge_center(bounds, edge);
let px = screen_space::norm_to_viewport(
screen_x,
screen_y,
bounds.half_extent_x,
bounds.half_extent_y,
viewport_size,
);
let above_line = px.y - LABEL_FONT_SIZE - LABEL_PIXEL_OFFSET;
match edge {
Edge::Left => ScreenPosition::new(LABEL_PIXEL_OFFSET, above_line),
Edge::Right => ScreenPosition::new(viewport_size.x - LABEL_PIXEL_OFFSET, above_line),
Edge::Top => ScreenPosition::new(px.x + LABEL_PIXEL_OFFSET, LABEL_PIXEL_OFFSET),
Edge::Bottom => ScreenPosition::new(
px.x + LABEL_PIXEL_OFFSET,
viewport_size.y - LABEL_PIXEL_OFFSET,
),
}
}
pub(super) fn bounds_label_position(upper_left: ScreenPosition) -> ScreenPosition {
ScreenPosition::new(
upper_left.x + LABEL_PIXEL_OFFSET,
upper_left.y - LABEL_FONT_SIZE - LABEL_PIXEL_OFFSET,
)
}
fn apply_margin_label_anchor(
node: &mut Node,
edge: Edge,
screen_pos: ScreenPosition,
viewport_size: Vec2,
) {
match edge {
Edge::Left | Edge::Top => {
node.left = Val::Px(screen_pos.x);
node.top = Val::Px(screen_pos.y);
node.right = Val::Auto;
node.bottom = Val::Auto;
},
Edge::Right => {
node.right = Val::Px(viewport_size.x - screen_pos.x);
node.top = Val::Px(screen_pos.y);
node.left = Val::Auto;
node.bottom = Val::Auto;
},
Edge::Bottom => {
node.left = Val::Px(screen_pos.x);
node.bottom = Val::Px(viewport_size.y - screen_pos.y);
node.right = Val::Auto;
node.top = Val::Auto;
},
}
}
fn margin_label_node(edge: Edge, screen_pos: ScreenPosition, viewport_size: Vec2) -> Node {
let mut node = Node {
position_type: PositionType::Absolute,
..default()
};
apply_margin_label_anchor(&mut node, edge, screen_pos, viewport_size);
node
}
pub(super) fn update_or_create_margin_label(
commands: &mut Commands,
label_query: &mut Query<(Entity, &MarginLabel, &mut Text, &mut Node, &mut TextColor)>,
params: MarginLabelParams,
) {
let existing = label_query
.iter_mut()
.find(|(_, label, _, _, _)| label.camera == params.camera && label.edge == params.edge);
if let Some((_, _, mut label_text, mut node, mut text_color)) = existing {
(**label_text).clone_from(¶ms.text);
text_color.0 = params.color;
apply_margin_label_anchor(
&mut node,
params.edge,
params.screen_pos,
params.viewport_size,
);
} else {
commands.spawn((
Text::new(params.text),
TextFont {
font_size: LABEL_FONT_SIZE,
..default()
},
TextColor(params.color),
margin_label_node(params.edge, params.screen_pos, params.viewport_size),
MarginLabel {
edge: params.edge,
camera: params.camera,
},
UiTargetCamera(params.camera),
));
}
}
pub(super) fn update_or_create_bounds_label(
commands: &mut Commands,
bounds_query: &mut Query<(Entity, &BoundsLabel, &mut Node), Without<MarginLabel>>,
camera: Entity,
screen_pos: ScreenPosition,
) {
let existing = bounds_query
.iter_mut()
.find(|(_, label, _)| label.camera == camera);
if let Some((_, _, mut node)) = existing {
node.left = Val::Px(screen_pos.x);
node.top = Val::Px(screen_pos.y);
} else {
commands.spawn((
Text::new("screen space bounds"),
TextFont {
font_size: LABEL_FONT_SIZE,
..default()
},
TextColor(Color::srgb(1.0, 1.0, 0.0)),
Node {
position_type: PositionType::Absolute,
left: Val::Px(screen_pos.x),
top: Val::Px(screen_pos.y),
..default()
},
BoundsLabel { camera },
UiTargetCamera(camera),
));
}
}