-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow for ui scaling #2808
Allow for ui scaling #2808
Changes from all commits
472808e
2940eda
143c804
50ab722
c3613e4
2eead4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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::*; | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I'm not if it's quite a zoom - in my mind it's closer to a shrinking of the logical UI window size. For me, a zoom suggests showing only part of the logical window, which this isn't intended for. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a dev I feel it more intuitive if one considers a zoom, since it effectively multiplies the size of the pixels shown just like a zoom would. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels better described to me as "setting UI scale", rather than a true zoom. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, should I remove the note? |
||
/// | ||
/// 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this whole 'example' section is potentially superfluous There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potentially, I prefer to make it clear though that one needs to put in |
||
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>() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
use bevy::{prelude::*, utils::Duration}; | ||
|
||
const SCALE_TIME: u64 = 400; | ||
|
||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, SystemLabel)] | ||
struct ApplyScaling; | ||
|
||
/// This example illustrates the UIScale resource from bevy_ui | ||
fn main() { | ||
App::new() | ||
.add_plugins(DefaultPlugins) | ||
.insert_resource(TargetScale { | ||
start_scale: 1.0, | ||
target_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>>, | ||
) { | ||
commands.spawn_bundle(UiCameraBundle::default()); | ||
|
||
let text_style = TextStyle { | ||
font: asset_server.load("fonts/FiraMono-Medium.ttf"), | ||
font_size: 16., | ||
color: Color::BLACK, | ||
}; | ||
|
||
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 { | ||
start_scale: f64, | ||
target_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 tick(&mut self, delta: Duration) -> &Self { | ||
self.target_time.tick(delta); | ||
self | ||
} | ||
|
||
fn already_completed(&self) -> bool { | ||
self.target_time.finished() && !self.target_time.just_finished() | ||
} | ||
} | ||
|
||
fn apply_scaling( | ||
time: Res<Time>, | ||
mut target_scale: ResMut<TargetScale>, | ||
mut ui_scale: ResMut<UiScale>, | ||
) { | ||
if target_scale.tick(time.delta()).already_completed() { | ||
return; | ||
} | ||
|
||
ui_scale.scale = target_scale.current_scale(); | ||
} | ||
|
||
fn ease_in_expo(x: f64) -> f64 { | ||
if x == 0. { | ||
0. | ||
} else { | ||
(2.0f64).powf(5. * x - 5.) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a conceptual reason this should be global instead of per-UI camera?
For example, I've needed a custom scale for different
vscode
windows because of different monitor scaling requirements - e.g. for presenting on one with notes on the other. Although I appreciate that is slightly different because those windows are different processes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a conceptual one no. So far ui-handling is not done per-window though, so I didn't go as far as understand how multiple window bevy works technically.