Skip to content

Commit

Permalink
Allow for ui scaling
Browse files Browse the repository at this point in the history
  • Loading branch information
TheNeikos committed Sep 11, 2021
1 parent 51a5070 commit 8c71310
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 8 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,10 @@ path = "examples/ui/text_debug.rs"
name = "ui"
path = "examples/ui/ui.rs"

[[example]]
name = "scaling"
path = "examples/ui/scaling.rs"

# Window
[[example]]
name = "clear_color"
Expand Down
11 changes: 6 additions & 5 deletions crates/bevy_ui/src/flex/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod convert;

use crate::{CalculatedSize, Node, Style};
use crate::{CalculatedSize, Node, Style, UiScale};
use bevy_app::EventReader;
use bevy_ecs::{
entity::Entity,
Expand Down Expand Up @@ -198,6 +198,7 @@ pub enum FlexError {
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
pub fn flex_node_system(
windows: Res<Windows>,
ui_scale: Res<UiScale>,
mut scale_factor_events: EventReader<WindowScaleFactorChanged>,
mut flex_surface: ResMut<FlexSurface>,
root_node_query: Query<Entity, (With<Node>, Without<Parent>)>,
Expand All @@ -222,14 +223,14 @@ pub fn flex_node_system(
1.
};

if scale_factor_events.iter().next_back().is_some() {
if scale_factor_events.iter().next_back().is_some() || ui_scale.is_changed() {
update_changed(
&mut *flex_surface,
logical_to_physical_factor,
logical_to_physical_factor * ui_scale.scale,
full_node_query,
);
} else {
update_changed(&mut *flex_surface, logical_to_physical_factor, node_query);
update_changed(&mut *flex_surface, logical_to_physical_factor * ui_scale.scale, node_query);
}

fn update_changed<F: WorldQuery>(
Expand All @@ -251,7 +252,7 @@ pub fn flex_node_system(
}

for (entity, style, calculated_size) in changed_size_query.iter() {
flex_surface.upsert_leaf(entity, style, *calculated_size, logical_to_physical_factor);
flex_surface.upsert_leaf(entity, style, *calculated_size, logical_to_physical_factor * ui_scale.scale);
}

// TODO: handle removed nodes
Expand Down
28 changes: 27 additions & 1 deletion crates/bevy_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ pub use ui_node::*;

pub mod prelude {
#[doc(hidden)]
pub use crate::{entity::*, ui_node::*, widget::Button, Anchors, Interaction, Margins};
pub use crate::{
entity::*, ui_node::*, widget::Button, Anchors, Interaction, Margins, UiScale,
};
}

use bevy_app::prelude::*;
Expand All @@ -39,9 +41,33 @@ pub enum UiSystem {
Focus,
}

#[derive(Debug)]
/// The current scale of the UI for all windows
///
/// ## Note
/// This is purely about the logical scale, and can
/// be considered like a zoom
///
/// This only affects pixel sizes, so a percent size will stay at that
pub struct UiScale {
/// The scale to be applied
///
/// # Example
///
/// A scale of `2.` will make every pixel size twice as large.
pub scale: f64,
}

impl Default for UiScale {
fn default() -> Self {
Self { scale: 1.0 }
}
}

impl Plugin for UiPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<FlexSurface>()
.init_resource::<UiScale>()
.register_type::<AlignContent>()
.register_type::<AlignItems>()
.register_type::<AlignSelf>()
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_ui/src/widget/text.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{CalculatedSize, Node, Style, Val};
use crate::{CalculatedSize, Node, Style, UiScale, Val};
use bevy_asset::Assets;
use bevy_ecs::{
entity::Entity,
Expand Down Expand Up @@ -49,6 +49,7 @@ pub fn text_system(
mut textures: ResMut<Assets<Texture>>,
fonts: Res<Assets<Font>>,
windows: Res<Windows>,
ui_scale: Res<UiScale>,
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<DefaultTextPipeline>,
Expand All @@ -62,7 +63,7 @@ pub fn text_system(
window.scale_factor()
} else {
1.
};
} * ui_scale.scale;

let inv_scale_factor = 1. / scale_factor;

Expand Down
142 changes: 142 additions & 0 deletions examples/ui/scaling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use bevy::{prelude::*, utils::Duration};

const SCALE_TIME: u64 = 400;

#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, SystemLabel)]
struct ApplyScaling;

/// This example illustrates the various features of Bevy UI.
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(TargetScale {
target_scale: 1.0,
start_scale: 1.0,
target_time: Timer::new(Duration::from_millis(SCALE_TIME), false),
})
.add_startup_system(setup)
.add_system(apply_scaling.label(ApplyScaling))
.add_system(change_scaling.before(ApplyScaling))
.run();
}

fn setup(
mut commands: Commands,
asset_server: ResMut<AssetServer>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
// ui camera
commands.spawn_bundle(UiCameraBundle::default());

let text_style = TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 16.,
color: Color::BLACK,
};

// root node
commands
.spawn_bundle(NodeBundle {
style: Style {
size: Size::new(Val::Percent(50.0), Val::Percent(50.0)),
position_type: PositionType::Absolute,
position: Rect {
left: Val::Percent(25.),
top: Val::Percent(25.),
..Default::default()
},
justify_content: JustifyContent::SpaceAround,
align_items: AlignItems::Center,
..Default::default()
},
material: materials.add(Color::ANTIQUE_WHITE.into()),
..Default::default()
})
.with_children(|parent| {
parent
.spawn_bundle(NodeBundle {
style: Style {
size: Size::new(Val::Px(40.), Val::Px(40.)),
..Default::default()
},
material: materials.add(Color::RED.into()),
..Default::default()
})
.with_children(|parent| {
parent.spawn_bundle(TextBundle {
text: Text::with_section("Size!", text_style, TextAlignment::default()),
..Default::default()
});
});
parent.spawn_bundle(NodeBundle {
style: Style {
size: Size::new(Val::Percent(15.), Val::Percent(15.)),
..Default::default()
},
material: materials.add(Color::BLUE.into()),
..Default::default()
});
parent.spawn_bundle(ImageBundle {
style: Style {
size: Size::new(Val::Px(30.0), Val::Px(30.0)),
..Default::default()
},
material: materials.add(asset_server.load("branding/icon.png").into()),
..Default::default()
});
});
}

fn change_scaling(input: Res<Input<KeyCode>>, mut ui_scale: ResMut<TargetScale>) {
if input.just_pressed(KeyCode::Up) {
let scale = (ui_scale.target_scale * 2.0).min(8.);
ui_scale.set_scale(scale);
info!("Scaling up! Scale: {}", ui_scale.target_scale);
}
if input.just_pressed(KeyCode::Down) {
let scale = (ui_scale.target_scale / 2.0).max(1. / 8.);
ui_scale.set_scale(scale);
info!("Scaling down! Scale: {}", ui_scale.target_scale);
}
}

struct TargetScale {
target_scale: f64,
start_scale: f64,
target_time: Timer,
}

impl TargetScale {
fn set_scale(&mut self, scale: f64) {
self.start_scale = self.current_scale();
self.target_scale = scale;
self.target_time.reset();
}

fn current_scale(&self) -> f64 {
let completion = self.target_time.percent();
let multiplier = ease_in_expo(completion as f64);
self.start_scale + (self.target_scale - self.start_scale) * multiplier
}
}

fn apply_scaling(
time: Res<Time>,
mut target_scale: ResMut<TargetScale>,
mut ui_scale: ResMut<UiScale>,
) {
let timer = target_scale.target_time.tick(time.delta());
if timer.finished() && !timer.just_finished() {
return;
}

ui_scale.scale = target_scale.current_scale();
}

fn ease_in_expo(x: f64) -> f64 {
if x == 0. {
0.
} else {
(2.0f64).powf(10. * x - 10.)
}
}

0 comments on commit 8c71310

Please sign in to comment.