Path: blob/main/examples/ui/images/image_node_resizing.rs
9331 views
//! This example demonstrates the behavior of `NodeImageMode::Auto` and `NodeImageMode::Stretch` by allowing keyboard input to resize an `ImageGroup` container.1//! It visually shows how images are sized automatically versus stretched to fit their container.23use bevy::{color::palettes::tailwind, prelude::*};45const MIN_RESIZE_VAL: f32 = 1.0;6const IMAGE_GROUP_BOX_MIN_WIDTH: f32 = 50.0;7const IMAGE_GROUP_BOX_MAX_WIDTH: f32 = 100.0;8const IMAGE_GROUP_BOX_MIN_HEIGHT: f32 = 10.0;9const IMAGE_GROUP_BOX_MAX_HEIGHT: f32 = 50.0;10const IMAGE_GROUP_BOX_INIT_WIDTH: f32 =11(IMAGE_GROUP_BOX_MIN_WIDTH + IMAGE_GROUP_BOX_MAX_WIDTH) / 2.;12const IMAGE_GROUP_BOX_INIT_HEIGHT: f32 =13(IMAGE_GROUP_BOX_MIN_HEIGHT + IMAGE_GROUP_BOX_MAX_HEIGHT) / 2.;14const TEXT_PREFIX: &str = "Compare NodeImageMode(Auto, Stretch) press `Up`/`Down` to resize height, press `Left`/`Right` to resize width\n";1516fn main() {17App::new()18.add_plugins(DefaultPlugins)19// Enable for image outline20.insert_resource(UiDebugOptions {21enabled: true,22..default()23})24.add_systems(Startup, setup)25.add_systems(Update, update)26.add_observer(on_trigger_image_group)27.run();28}2930#[derive(Debug, Component)]31struct ImageGroup;3233#[derive(Debug, Event)]34enum ImageGroupResize {35HeightGrow,36HeightShrink,37WidthGrow,38WidthShrink,39}4041// Text data for easy modification42#[derive(Debug, Component)]43struct TextData {44height: f32,45width: f32,46}4748#[derive(Debug)]49enum Direction {50Height,51Width,52}5354#[derive(Debug, EntityEvent)]55struct TextUpdate {56entity: Entity,57direction: Direction,58change: f32,59}6061fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {62let image_handle = asset_server.load("branding/icon.png");63let full_text = format!(64"{}height : {}%, width : {}%",65TEXT_PREFIX, IMAGE_GROUP_BOX_INIT_HEIGHT, IMAGE_GROUP_BOX_INIT_WIDTH,66);6768commands.spawn(Camera2d);6970let container = commands71.spawn((72Node {73display: Display::Grid,74width: percent(100),75height: percent(100),76grid_template_rows: vec![GridTrack::min_content(), GridTrack::flex(1.0)],77..default()78},79BackgroundColor(Color::WHITE),80))81.id();8283// Keyboard Text84commands85.spawn((86TextData {87height: IMAGE_GROUP_BOX_INIT_HEIGHT,88width: IMAGE_GROUP_BOX_INIT_WIDTH,89},90Text::new(full_text),91TextColor::BLACK,92Node {93grid_row: GridPlacement::span(1),94padding: px(6).all(),95..default()96},97UiDebugOptions {98enabled: false,99..default()100},101ChildOf(container),102))103.observe(update_text);104105commands106.spawn((107Node {108display: Display::Flex,109grid_row: GridPlacement::span(1),110flex_direction: FlexDirection::Column,111justify_content: JustifyContent::SpaceAround,112padding: px(10.).all(),113..default()114},115BackgroundColor(Color::BLACK),116ChildOf(container),117))118.with_children(|builder| {119// `NodeImageMode::Auto` will resize the image automatically by taking the size of the source image and applying any layout constraints.120builder121.spawn((122ImageGroup,123Node {124display: Display::Flex,125justify_content: JustifyContent::Start,126width: percent(IMAGE_GROUP_BOX_INIT_WIDTH),127height: percent(IMAGE_GROUP_BOX_INIT_HEIGHT),128..default()129},130BackgroundColor(Color::from(tailwind::BLUE_100)),131))132.with_children(|parent| {133for _ in 0..4 {134// child node will apply Flex layout135parent.spawn((136Node::default(),137ImageNode {138image: image_handle.clone(),139image_mode: NodeImageMode::Auto,140..default()141},142));143}144});145// `NodeImageMode::Stretch` will resize the image to match the size of the `Node` component146builder147.spawn((148ImageGroup,149Node {150display: Display::Flex,151justify_content: JustifyContent::Start,152width: percent(IMAGE_GROUP_BOX_INIT_WIDTH),153height: percent(IMAGE_GROUP_BOX_INIT_HEIGHT),154..default()155},156BackgroundColor(Color::from(tailwind::BLUE_100)),157))158.with_children(|parent| {159for width in [10., 20., 30., 40.] {160parent.spawn((161Node {162height: percent(100),163width: percent(width),164..default()165},166ImageNode {167image: image_handle.clone(),168image_mode: NodeImageMode::Stretch,169..default()170},171));172}173});174});175}176177// Trigger event178fn update(179keycode: Res<ButtonInput<KeyCode>>,180mut commands: Commands,181query: Query<Entity, With<TextData>>,182) {183let entity = query.single().unwrap();184if keycode.pressed(KeyCode::ArrowUp) {185commands.trigger(ImageGroupResize::HeightGrow);186commands.trigger(TextUpdate {187entity,188direction: Direction::Height,189change: MIN_RESIZE_VAL,190});191}192if keycode.pressed(KeyCode::ArrowDown) {193commands.trigger(ImageGroupResize::HeightShrink);194commands.trigger(TextUpdate {195entity,196direction: Direction::Height,197change: -MIN_RESIZE_VAL,198});199}200if keycode.pressed(KeyCode::ArrowLeft) {201commands.trigger(ImageGroupResize::WidthShrink);202commands.trigger(TextUpdate {203entity,204direction: Direction::Width,205change: -MIN_RESIZE_VAL,206});207}208if keycode.pressed(KeyCode::ArrowRight) {209commands.trigger(ImageGroupResize::WidthGrow);210commands.trigger(TextUpdate {211entity,212direction: Direction::Width,213change: MIN_RESIZE_VAL,214});215}216}217218fn update_text(219event: On<TextUpdate>,220mut textmeta: Single<&mut TextData>,221mut text: Single<&mut Text>,222) {223let mut new_text = Text::new(TEXT_PREFIX);224match event.direction {225Direction::Height => {226textmeta.height = (textmeta.height + event.change)227.clamp(IMAGE_GROUP_BOX_MIN_HEIGHT, IMAGE_GROUP_BOX_MAX_HEIGHT);228new_text.push_str(&format!(229"height : {}%, width : {}%",230textmeta.height, textmeta.width231));232}233Direction::Width => {234textmeta.width = (textmeta.width + event.change)235.clamp(IMAGE_GROUP_BOX_MIN_WIDTH, IMAGE_GROUP_BOX_MAX_WIDTH);236new_text.push_str(&format!(237"height : {}%, width : {}%",238textmeta.height, textmeta.width239));240}241}242text.0 = new_text.0;243}244245fn on_trigger_image_group(event: On<ImageGroupResize>, query: Query<&mut Node, With<ImageGroup>>) {246for mut node in query {247match event.event() {248ImageGroupResize::HeightGrow => {249if let Val::Percent(val) = &mut node.height {250*val = (*val + MIN_RESIZE_VAL).min(IMAGE_GROUP_BOX_MAX_HEIGHT);251}252}253ImageGroupResize::HeightShrink => {254if let Val::Percent(val) = &mut node.height {255*val = (*val - MIN_RESIZE_VAL).max(IMAGE_GROUP_BOX_MIN_HEIGHT);256}257}258ImageGroupResize::WidthGrow => {259if let Val::Percent(val) = &mut node.width {260*val = (*val + MIN_RESIZE_VAL).min(IMAGE_GROUP_BOX_MAX_WIDTH);261}262}263ImageGroupResize::WidthShrink => {264if let Val::Percent(val) = &mut node.width {265*val = (*val - MIN_RESIZE_VAL).max(IMAGE_GROUP_BOX_MIN_WIDTH);266}267}268}269}270}271272273