mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 02:33:54 +03:00
layout uses logical pixels, not physical
Previously it was not clear (to me at least) when a value used in layout was in units of logical pixels vs physical pixels. This lead to some ambiguity about where to scale values, and lead to some values being scaled more than once or sometimes not at all, leading to inconsistent layouts across DPI's. The intent of this change is to solve this ambiguity by having the ui clients work *exlusively* with logical pixels. To achieve this, we consolidate all scaling to the graphics backend. We translate all PhysicalPositions from the windowing libraries to LogicalPixles. Our own types: ScreenPt, ScreenDim, etc. are all in logical units. In some places, I replaced passing raw floats with a corresponding Screen* type to clarify that the units are in logical pixels.
This commit is contained in:
parent
9ce7ab1c88
commit
f42b7c003c
@ -11,22 +11,20 @@ use usvg::Options;
|
|||||||
// TODO We don't need refcell maybe? Can we take &mut Assets?
|
// TODO We don't need refcell maybe? Can we take &mut Assets?
|
||||||
pub struct Assets {
|
pub struct Assets {
|
||||||
pub default_line_height: RefCell<f64>,
|
pub default_line_height: RefCell<f64>,
|
||||||
pub scale_factor: RefCell<f64>,
|
|
||||||
text_cache: RefCell<LruCache<String, GeomBatch>>,
|
text_cache: RefCell<LruCache<String, GeomBatch>>,
|
||||||
line_height_cache: RefCell<HashMap<(Font, usize), f64>>,
|
line_height_cache: RefCell<HashMap<(Font, usize), f64>>,
|
||||||
// Keyed by filename, then scale factor mangled into a hashable form. Tuple doesn't work
|
// Keyed by filename, then scale factor mangled into a hashable form. Tuple doesn't work
|
||||||
// because of borrowing.
|
// because of borrowing.
|
||||||
svg_cache: RefCell<HashMap<String, HashMap<usize, (GeomBatch, Bounds)>>>,
|
svg_cache: RefCell<HashMap<String, (GeomBatch, Bounds)>>,
|
||||||
#[cfg(not(feature = "wasm-backend"))]
|
#[cfg(not(feature = "wasm-backend"))]
|
||||||
font_to_id: HashMap<Font, fontdb::ID>,
|
font_to_id: HashMap<Font, fontdb::ID>,
|
||||||
pub text_opts: Options,
|
pub text_opts: Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Assets {
|
impl Assets {
|
||||||
pub fn new(font_dir: String, scale_factor: f64) -> Assets {
|
pub fn new(font_dir: String) -> Assets {
|
||||||
let mut a = Assets {
|
let mut a = Assets {
|
||||||
default_line_height: RefCell::new(0.0),
|
default_line_height: RefCell::new(0.0),
|
||||||
scale_factor: RefCell::new(scale_factor),
|
|
||||||
text_cache: RefCell::new(LruCache::new(500)),
|
text_cache: RefCell::new(LruCache::new(500)),
|
||||||
line_height_cache: RefCell::new(HashMap::new()),
|
line_height_cache: RefCell::new(HashMap::new()),
|
||||||
svg_cache: RefCell::new(HashMap::new()),
|
svg_cache: RefCell::new(HashMap::new()),
|
||||||
@ -89,7 +87,7 @@ impl Assets {
|
|||||||
((ascent - descent) as f64) * scale
|
((ascent - descent) as f64) * scale
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let height = text::SCALE_LINE_HEIGHT * *self.scale_factor.borrow() * line_height;
|
let height = text::SCALE_LINE_HEIGHT * line_height;
|
||||||
|
|
||||||
self.line_height_cache.borrow_mut().insert(key, height);
|
self.line_height_cache.borrow_mut().insert(key, height);
|
||||||
height
|
height
|
||||||
@ -113,29 +111,11 @@ impl Assets {
|
|||||||
self.text_cache.borrow_mut().put(key, geom);
|
self.text_cache.borrow_mut().put(key, geom);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cached_svg(&self, key: &str, scale_factor: f64) -> Option<(GeomBatch, Bounds)> {
|
pub fn get_cached_svg(&self, key: &str) -> Option<(GeomBatch, Bounds)> {
|
||||||
self.svg_cache
|
self.svg_cache.borrow().get(key).cloned()
|
||||||
.borrow()
|
|
||||||
.get(key)
|
|
||||||
.and_then(|m| m.get(&key_scale_factor(scale_factor)).cloned())
|
|
||||||
}
|
|
||||||
pub fn cache_svg(&self, key: String, scale_factor: f64, geom: GeomBatch, bounds: Bounds) {
|
|
||||||
self.svg_cache
|
|
||||||
.borrow_mut()
|
|
||||||
.entry(key)
|
|
||||||
.or_insert_with(HashMap::new)
|
|
||||||
.insert(key_scale_factor(scale_factor), (geom, bounds));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_scale_factor(&self, scale_factor: f64) {
|
pub fn cache_svg(&self, key: String, geom: GeomBatch, bounds: Bounds) {
|
||||||
*self.scale_factor.borrow_mut() = scale_factor;
|
self.svg_cache.borrow_mut().insert(key, (geom, bounds));
|
||||||
self.text_cache.borrow_mut().clear();
|
|
||||||
self.line_height_cache.borrow_mut().clear();
|
|
||||||
*self.default_line_height.borrow_mut() =
|
|
||||||
self.line_height(text::DEFAULT_FONT, text::DEFAULT_FONT_SIZE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_scale_factor(x: f64) -> usize {
|
|
||||||
(x * 100.0) as usize
|
|
||||||
}
|
|
||||||
|
@ -4,13 +4,7 @@ use glium::uniforms::UniformValue;
|
|||||||
use glium::Surface;
|
use glium::Surface;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
pub fn setup(
|
pub fn setup(window_title: &str) -> (PrerenderInnards, winit::event_loop::EventLoop<()>) {
|
||||||
window_title: &str,
|
|
||||||
) -> (
|
|
||||||
PrerenderInnards,
|
|
||||||
winit::event_loop::EventLoop<()>,
|
|
||||||
ScreenDims,
|
|
||||||
) {
|
|
||||||
let event_loop = winit::event_loop::EventLoop::new();
|
let event_loop = winit::event_loop::EventLoop::new();
|
||||||
let display = match glium::Display::new(
|
let display = match glium::Display::new(
|
||||||
winit::window::WindowBuilder::new()
|
winit::window::WindowBuilder::new()
|
||||||
@ -105,19 +99,6 @@ pub fn setup(
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let inner_window = display.gl_window().window().inner_size();
|
|
||||||
let monitor = event_loop.primary_monitor().size();
|
|
||||||
let initial_size = if cfg!(target_os = "linux") {
|
|
||||||
monitor
|
|
||||||
} else {
|
|
||||||
inner_window
|
|
||||||
};
|
|
||||||
println!(
|
|
||||||
"Inner window size is {:?}, monitor is {:?}, scale factor is {}",
|
|
||||||
inner_window,
|
|
||||||
monitor,
|
|
||||||
display.gl_window().window().scale_factor()
|
|
||||||
);
|
|
||||||
(
|
(
|
||||||
PrerenderInnards {
|
PrerenderInnards {
|
||||||
display,
|
display,
|
||||||
@ -125,7 +106,6 @@ pub fn setup(
|
|||||||
total_bytes_uploaded: Cell::new(0),
|
total_bytes_uploaded: Cell::new(0),
|
||||||
},
|
},
|
||||||
event_loop,
|
event_loop,
|
||||||
ScreenDims::new(initial_size.width.into(), initial_size.height.into()),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,18 +146,15 @@ impl<'a> GfxCtxInnards<'a> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enable_clipping(&mut self, rect: ScreenRectangle, canvas: &Canvas) {
|
pub fn enable_clipping(&mut self, rect: ScreenRectangle, scale_factor: f64, canvas: &Canvas) {
|
||||||
assert!(self.params.scissor.is_none());
|
assert!(self.params.scissor.is_none());
|
||||||
// The scissor rectangle has to be in device coordinates, so you would think some transform
|
// The scissor rectangle is in units of physical pixles, as opposed to logical pixels
|
||||||
// by scale factor (previously called HiDPI factor) has to happen here. But actually,
|
|
||||||
// window dimensions and the rectangle passed in are already scaled up. So don't do
|
|
||||||
// anything here!
|
|
||||||
self.params.scissor = Some(glium::Rect {
|
self.params.scissor = Some(glium::Rect {
|
||||||
left: rect.x1 as u32,
|
left: (rect.x1 * scale_factor) as u32,
|
||||||
// Y-inversion
|
// Y-inversion
|
||||||
bottom: (canvas.window_height - rect.y2) as u32,
|
bottom: ((canvas.window_height - rect.y2) * scale_factor) as u32,
|
||||||
width: (rect.x2 - rect.x1) as u32,
|
width: ((rect.x2 - rect.x1) * scale_factor) as u32,
|
||||||
height: (rect.y2 - rect.y1) as u32,
|
height: ((rect.y2 - rect.y1) * scale_factor) as u32,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,11 +277,15 @@ impl PrerenderInnards {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_resized(&self, _: f64, _: f64) {}
|
pub fn window_resized(&self, _new_size: ScreenDims) {}
|
||||||
|
|
||||||
pub fn get_inner_size(&self) -> (f64, f64) {
|
pub fn window_size(&self, scale_factor: f64) -> ScreenDims {
|
||||||
let size = self.display.gl_window().window().inner_size();
|
self.display
|
||||||
(size.width.into(), size.height.into())
|
.gl_window()
|
||||||
|
.window()
|
||||||
|
.inner_size()
|
||||||
|
.to_logical(scale_factor)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_window_icon(&self, icon: winit::window::Icon) {
|
pub fn set_window_icon(&self, icon: winit::window::Icon) {
|
||||||
|
@ -312,14 +312,15 @@ impl PrerenderInnards {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_resized(&self, width: f64, height: f64) {
|
pub fn window_resized(&self, new_size: ScreenDims) {
|
||||||
self.windowed_context
|
todo!("DPI TODO: support other backends");
|
||||||
.resize(winit::dpi::PhysicalSize::new(width as u32, height as u32));
|
//self.windowed_context
|
||||||
unsafe {
|
// .resize(winit::dpi::PhysicalSize::new(width as u32, height as u32));
|
||||||
self.gl.viewport(0, 0, width as i32, height as i32);
|
//unsafe {
|
||||||
// I think it's safe to assume there's not a clip right now.
|
// self.gl.viewport(0, 0, width as i32, height as i32);
|
||||||
self.gl.scissor(0, 0, width as i32, height as i32);
|
// // I think it's safe to assume there's not a clip right now.
|
||||||
}
|
// self.gl.scissor(0, 0, width as i32, height as i32);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inner_size(&self) -> (f64, f64) {
|
pub fn get_inner_size(&self) -> (f64, f64) {
|
||||||
|
@ -316,12 +316,13 @@ impl PrerenderInnards {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn window_resized(&self, width: f64, height: f64) {
|
pub fn window_resized(&self, new_size: ScreenDims) {
|
||||||
unsafe {
|
todo!("DPI TODO: support other backends");
|
||||||
self.gl.viewport(0, 0, width as i32, height as i32);
|
//unsafe {
|
||||||
// I think it's safe to assume there's not a clip right now.
|
// self.gl.viewport(0, 0, width as i32, height as i32);
|
||||||
self.gl.scissor(0, 0, width as i32, height as i32);
|
// // I think it's safe to assume there's not a clip right now.
|
||||||
}
|
// self.gl.scissor(0, 0, width as i32, height as i32);
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_inner_size(&self) -> (f64, f64) {
|
pub fn get_inner_size(&self) -> (f64, f64) {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use crate::assets::Assets;
|
|
||||||
use crate::{Key, ScreenDims, ScreenPt, ScreenRectangle, UpdateType, UserInput};
|
use crate::{Key, ScreenDims, ScreenPt, ScreenRectangle, UpdateType, UserInput};
|
||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
use geom::{Bounds, Pt2D};
|
use geom::{Bounds, Pt2D};
|
||||||
@ -49,7 +48,7 @@ pub struct Canvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Canvas {
|
impl Canvas {
|
||||||
pub(crate) fn new(initial_width: f64, initial_height: f64) -> Canvas {
|
pub(crate) fn new(initial_dims: ScreenDims) -> Canvas {
|
||||||
Canvas {
|
Canvas {
|
||||||
cam_x: 0.0,
|
cam_x: 0.0,
|
||||||
cam_y: 0.0,
|
cam_y: 0.0,
|
||||||
@ -62,8 +61,8 @@ impl Canvas {
|
|||||||
drag_canvas_from: None,
|
drag_canvas_from: None,
|
||||||
drag_just_ended: false,
|
drag_just_ended: false,
|
||||||
|
|
||||||
window_width: initial_width,
|
window_width: initial_dims.width,
|
||||||
window_height: initial_height,
|
window_height: initial_dims.height,
|
||||||
|
|
||||||
map_dims: (0.0, 0.0),
|
map_dims: (0.0, 0.0),
|
||||||
invert_scroll: false,
|
invert_scroll: false,
|
||||||
@ -265,6 +264,10 @@ impl Canvas {
|
|||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_window_dims(&self) -> ScreenDims {
|
||||||
|
ScreenDims::new(self.window_width, self.window_height)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_map_bounds(&self) -> Bounds {
|
fn get_map_bounds(&self) -> Bounds {
|
||||||
let mut b = Bounds::new();
|
let mut b = Bounds::new();
|
||||||
b.update(Pt2D::new(0.0, 0.0));
|
b.update(Pt2D::new(0.0, 0.0));
|
||||||
@ -306,7 +309,6 @@ impl Canvas {
|
|||||||
|
|
||||||
pub(crate) fn align_window(
|
pub(crate) fn align_window(
|
||||||
&self,
|
&self,
|
||||||
assets: &Assets,
|
|
||||||
dims: ScreenDims,
|
dims: ScreenDims,
|
||||||
horiz: HorizontalAlignment,
|
horiz: HorizontalAlignment,
|
||||||
vert: VerticalAlignment,
|
vert: VerticalAlignment,
|
||||||
@ -323,9 +325,7 @@ impl Canvas {
|
|||||||
VerticalAlignment::Center => (self.window_height - dims.height) / 2.0,
|
VerticalAlignment::Center => (self.window_height - dims.height) / 2.0,
|
||||||
VerticalAlignment::Bottom => self.window_height - dims.height,
|
VerticalAlignment::Bottom => self.window_height - dims.height,
|
||||||
// TODO Hack
|
// TODO Hack
|
||||||
VerticalAlignment::BottomAboveOSD => {
|
VerticalAlignment::BottomAboveOSD => self.window_height - dims.height - 60.0,
|
||||||
self.window_height - dims.height - 60.0 * *assets.scale_factor.borrow()
|
|
||||||
}
|
|
||||||
VerticalAlignment::Percent(pct) => pct * self.window_height,
|
VerticalAlignment::Percent(pct) => pct * self.window_height,
|
||||||
VerticalAlignment::Above(y) => y - dims.height,
|
VerticalAlignment::Above(y) => y - dims.height,
|
||||||
VerticalAlignment::Below(y) => y,
|
VerticalAlignment::Below(y) => y,
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
Canvas, Color, Drawable, GeomBatch, ScreenDims, ScreenPt, ScreenRectangle, Style, Text,
|
Canvas, Color, Drawable, GeomBatch, ScreenDims, ScreenPt, ScreenRectangle, Style, Text,
|
||||||
};
|
};
|
||||||
use geom::{Bounds, Polygon, Pt2D};
|
use geom::{Bounds, Polygon, Pt2D};
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, RefCell};
|
||||||
|
|
||||||
// Lower is more on top
|
// Lower is more on top
|
||||||
const MAPSPACE_Z: f32 = 1.0;
|
const MAPSPACE_Z: f32 = 1.0;
|
||||||
@ -136,7 +136,8 @@ impl<'a> GfxCtx<'a> {
|
|||||||
|
|
||||||
// TODO Stateful API :(
|
// TODO Stateful API :(
|
||||||
pub fn enable_clipping(&mut self, rect: ScreenRectangle) {
|
pub fn enable_clipping(&mut self, rect: ScreenRectangle) {
|
||||||
self.inner.enable_clipping(rect, self.canvas);
|
let scale_factor = self.prerender.get_scale_factor();
|
||||||
|
self.inner.enable_clipping(rect, scale_factor, self.canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_clipping(&mut self) {
|
pub fn disable_clipping(&mut self) {
|
||||||
@ -227,6 +228,7 @@ pub struct Prerender {
|
|||||||
pub(crate) inner: PrerenderInnards,
|
pub(crate) inner: PrerenderInnards,
|
||||||
pub(crate) assets: Assets,
|
pub(crate) assets: Assets,
|
||||||
pub(crate) num_uploads: Cell<usize>,
|
pub(crate) num_uploads: Cell<usize>,
|
||||||
|
pub scale_factor: RefCell<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Prerender {
|
impl Prerender {
|
||||||
@ -251,4 +253,16 @@ impl Prerender {
|
|||||||
pub(crate) fn request_redraw(&self) {
|
pub(crate) fn request_redraw(&self) {
|
||||||
self.inner.request_redraw()
|
self.inner.request_redraw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_scale_factor(&self) -> f64 {
|
||||||
|
*self.scale_factor.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_scale_factor(&self, scale_factor: f64) {
|
||||||
|
*self.scale_factor.borrow_mut() = scale_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window_size(&self) -> ScreenDims {
|
||||||
|
self.inner.window_size(self.get_scale_factor())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::ScreenPt;
|
use crate::{ScreenDims, ScreenPt};
|
||||||
use geom::Duration;
|
use geom::Duration;
|
||||||
use winit::event::{
|
use winit::event::{
|
||||||
ElementState, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent,
|
ElementState, KeyboardInput, MouseButton, MouseScrollDelta, VirtualKeyCode, WindowEvent,
|
||||||
@ -23,11 +23,11 @@ pub enum Event {
|
|||||||
WindowLostCursor,
|
WindowLostCursor,
|
||||||
WindowGainedCursor,
|
WindowGainedCursor,
|
||||||
MouseWheelScroll(f64, f64),
|
MouseWheelScroll(f64, f64),
|
||||||
WindowResized(f64, f64),
|
WindowResized(ScreenDims),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
pub fn from_winit_event(ev: WindowEvent) -> Option<Event> {
|
pub fn from_winit_event(ev: WindowEvent, scale_factor: f64) -> Option<Event> {
|
||||||
match ev {
|
match ev {
|
||||||
WindowEvent::MouseInput { state, button, .. } => match (button, state) {
|
WindowEvent::MouseInput { state, button, .. } => match (button, state) {
|
||||||
(MouseButton::Left, ElementState::Pressed) => Some(Event::LeftMouseButtonDown),
|
(MouseButton::Left, ElementState::Pressed) => Some(Event::LeftMouseButtonDown),
|
||||||
@ -47,9 +47,9 @@ impl Event {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::CursorMoved { position, .. } => {
|
WindowEvent::CursorMoved { position, .. } => Some(Event::MouseMovedTo(
|
||||||
Some(Event::MouseMovedTo(ScreenPt::new(position.x, position.y)))
|
position.to_logical(scale_factor).into(),
|
||||||
}
|
)),
|
||||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||||
MouseScrollDelta::LineDelta(dx, dy) => {
|
MouseScrollDelta::LineDelta(dx, dy) => {
|
||||||
if dx == 0.0 && dy == 0.0 {
|
if dx == 0.0 && dy == 0.0 {
|
||||||
@ -72,7 +72,7 @@ impl Event {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
WindowEvent::Resized(size) => {
|
WindowEvent::Resized(size) => {
|
||||||
Some(Event::WindowResized(size.width.into(), size.height.into()))
|
Some(Event::WindowResized(size.to_logical(scale_factor).into()))
|
||||||
}
|
}
|
||||||
WindowEvent::Focused(gained) => Some(if gained {
|
WindowEvent::Focused(gained) => Some(if gained {
|
||||||
Event::WindowGainedCursor
|
Event::WindowGainedCursor
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
svg, text, Canvas, Color, Drawable, Event, GeomBatch, GfxCtx, Line, Prerender, ScreenPt, Style,
|
svg, text, Canvas, Color, Drawable, Event, GeomBatch, GfxCtx, Line, Prerender, ScreenDims,
|
||||||
Text, UserInput,
|
ScreenPt, Style, Text, UserInput,
|
||||||
};
|
};
|
||||||
use abstutil::{elapsed_seconds, Timer, TimerSink};
|
use abstutil::{elapsed_seconds, Timer, TimerSink};
|
||||||
use geom::Polygon;
|
use geom::Polygon;
|
||||||
@ -41,8 +41,7 @@ impl<'a> EventCtx<'a> {
|
|||||||
&timer_name,
|
&timer_name,
|
||||||
Box::new(LoadingScreen::new(
|
Box::new(LoadingScreen::new(
|
||||||
self.prerender,
|
self.prerender,
|
||||||
self.canvas.window_width,
|
self.canvas.get_window_dims(),
|
||||||
self.canvas.window_height,
|
|
||||||
timer_name.clone(),
|
timer_name.clone(),
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
@ -113,18 +112,6 @@ impl<'a> EventCtx<'a> {
|
|||||||
self.prerender.upload(batch)
|
self.prerender.upload(batch)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_scale_factor(&self, scale: f64) {
|
|
||||||
self.prerender.assets.set_scale_factor(scale)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_scale_factor(&self) -> f64 {
|
|
||||||
*self.prerender.assets.scale_factor.borrow()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn monitor_scale_factor(&self) -> f64 {
|
|
||||||
self.prerender.inner.monitor_scale_factor()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn cursor_clickable(&mut self) {
|
pub(crate) fn cursor_clickable(&mut self) {
|
||||||
self.prerender
|
self.prerender
|
||||||
.inner
|
.inner
|
||||||
@ -153,13 +140,12 @@ pub struct LoadingScreen<'a> {
|
|||||||
impl<'a> LoadingScreen<'a> {
|
impl<'a> LoadingScreen<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
prerender: &'a Prerender,
|
prerender: &'a Prerender,
|
||||||
initial_width: f64,
|
initial_size: ScreenDims,
|
||||||
initial_height: f64,
|
|
||||||
title: String,
|
title: String,
|
||||||
) -> LoadingScreen<'a> {
|
) -> LoadingScreen<'a> {
|
||||||
let canvas = Canvas::new(initial_width, initial_height);
|
let canvas = Canvas::new(initial_size);
|
||||||
let max_capacity =
|
let max_capacity =
|
||||||
(0.8 * initial_height / *prerender.assets.default_line_height.borrow()) as usize;
|
(0.8 * initial_size.height / *prerender.assets.default_line_height.borrow()) as usize;
|
||||||
LoadingScreen {
|
LoadingScreen {
|
||||||
prerender,
|
prerender,
|
||||||
lines: VecDeque::new(),
|
lines: VecDeque::new(),
|
||||||
|
@ -137,23 +137,18 @@ impl GeomBatch {
|
|||||||
pub fn from_svg_contents(raw: Vec<u8>) -> GeomBatch {
|
pub fn from_svg_contents(raw: Vec<u8>) -> GeomBatch {
|
||||||
let mut batch = GeomBatch::new();
|
let mut batch = GeomBatch::new();
|
||||||
let svg_tree = usvg::Tree::from_data(&raw, &usvg::Options::default()).unwrap();
|
let svg_tree = usvg::Tree::from_data(&raw, &usvg::Options::default()).unwrap();
|
||||||
svg::add_svg_inner(&mut batch, svg_tree, svg::HIGH_QUALITY, 1.0).unwrap();
|
svg::add_svg_inner(&mut batch, svg_tree, svg::HIGH_QUALITY).unwrap();
|
||||||
batch
|
batch
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a batch containing an SVG from a file.
|
/// Returns a batch containing an SVG from a file.
|
||||||
pub fn mapspace_svg(prerender: &Prerender, filename: &str) -> GeomBatch {
|
pub fn mapspace_svg(prerender: &Prerender, filename: &str) -> GeomBatch {
|
||||||
svg::load_svg(prerender, filename, 1.0).0
|
svg::load_svg(prerender, filename).0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a batch containing an SVG from a file. Uses the current screen's scale factor.
|
/// Returns a batch containing an SVG from a file.
|
||||||
pub fn screenspace_svg<I: Into<String>>(prerender: &Prerender, filename: I) -> GeomBatch {
|
pub fn screenspace_svg<I: Into<String>>(prerender: &Prerender, filename: I) -> GeomBatch {
|
||||||
svg::load_svg(
|
svg::load_svg(prerender, &filename.into()).0
|
||||||
prerender,
|
|
||||||
&filename.into(),
|
|
||||||
*prerender.assets.scale_factor.borrow(),
|
|
||||||
)
|
|
||||||
.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms all colors in a batch.
|
/// Transforms all colors in a batch.
|
||||||
|
@ -101,7 +101,7 @@ impl UserInput {
|
|||||||
|
|
||||||
pub fn is_window_resized(&self) -> bool {
|
pub fn is_window_resized(&self) -> bool {
|
||||||
match self.event {
|
match self.event {
|
||||||
Event::WindowResized(_, _) => true,
|
Event::WindowResized(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ impl Widget {
|
|||||||
// TODO These are literally just convenient APIs to avoid importing JustDraw. Do we want this
|
// TODO These are literally just convenient APIs to avoid importing JustDraw. Do we want this
|
||||||
// or not?
|
// or not?
|
||||||
pub fn draw_batch(ctx: &EventCtx, batch: GeomBatch) -> Widget {
|
pub fn draw_batch(ctx: &EventCtx, batch: GeomBatch) -> Widget {
|
||||||
JustDraw::wrap(ctx, batch.scale(ctx.get_scale_factor()))
|
JustDraw::wrap(ctx, batch)
|
||||||
}
|
}
|
||||||
pub fn draw_svg<I: Into<String>>(ctx: &EventCtx, filename: I) -> Widget {
|
pub fn draw_svg<I: Into<String>>(ctx: &EventCtx, filename: I) -> Widget {
|
||||||
JustDraw::svg(ctx, filename.into())
|
JustDraw::svg(ctx, filename.into())
|
||||||
@ -307,9 +307,8 @@ impl Widget {
|
|||||||
// TODO 35 is a sad magic number. By default, Composites have padding of 16, so assuming
|
// TODO 35 is a sad magic number. By default, Composites have padding of 16, so assuming
|
||||||
// this geometry is going in one of those, it makes sense to subtract 32. But that still
|
// this geometry is going in one of those, it makes sense to subtract 32. But that still
|
||||||
// caused some scrolling in a test, so snip away a few more pixels.
|
// caused some scrolling in a test, so snip away a few more pixels.
|
||||||
self.layout.style.min_size.width = Dimension::Points(
|
self.layout.style.min_size.width =
|
||||||
(w * ctx.canvas.window_width * ctx.get_scale_factor()) as f32 - 35.0,
|
Dimension::Points((w * ctx.canvas.window_width) as f32 - 35.0);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pretend we're in a Composite and basically copy recompute_layout
|
// Pretend we're in a Composite and basically copy recompute_layout
|
||||||
@ -325,12 +324,7 @@ impl Widget {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut nodes = vec![];
|
let mut nodes = vec![];
|
||||||
self.get_flexbox(
|
self.get_flexbox(root, &mut stretch, &mut nodes);
|
||||||
root,
|
|
||||||
ctx.get_scale_factor() as f32,
|
|
||||||
&mut stretch,
|
|
||||||
&mut nodes,
|
|
||||||
);
|
|
||||||
nodes.reverse();
|
nodes.reverse();
|
||||||
|
|
||||||
let container_size = Size {
|
let container_size = Size {
|
||||||
@ -371,13 +365,7 @@ impl Widget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Populate a flattened list of Nodes, matching the traversal order
|
// Populate a flattened list of Nodes, matching the traversal order
|
||||||
fn get_flexbox(
|
fn get_flexbox(&self, parent: Node, stretch: &mut Stretch, nodes: &mut Vec<Node>) {
|
||||||
&self,
|
|
||||||
parent: Node,
|
|
||||||
scale_factor: f32,
|
|
||||||
stretch: &mut Stretch,
|
|
||||||
nodes: &mut Vec<Node>,
|
|
||||||
) {
|
|
||||||
if let Some(container) = self.widget.downcast_ref::<Container>() {
|
if let Some(container) = self.widget.downcast_ref::<Container>() {
|
||||||
let mut style = self.layout.style.clone();
|
let mut style = self.layout.style.clone();
|
||||||
style.flex_direction = if container.is_row {
|
style.flex_direction = if container.is_row {
|
||||||
@ -388,7 +376,7 @@ impl Widget {
|
|||||||
let node = stretch.new_node(style, Vec::new()).unwrap();
|
let node = stretch.new_node(style, Vec::new()).unwrap();
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
for widget in &container.members {
|
for widget in &container.members {
|
||||||
widget.get_flexbox(node, scale_factor, stretch, nodes);
|
widget.get_flexbox(node, stretch, nodes);
|
||||||
}
|
}
|
||||||
stretch.add_child(parent, node).unwrap();
|
stretch.add_child(parent, node).unwrap();
|
||||||
return;
|
return;
|
||||||
@ -398,32 +386,6 @@ impl Widget {
|
|||||||
width: Dimension::Points(self.widget.get_dims().width as f32),
|
width: Dimension::Points(self.widget.get_dims().width as f32),
|
||||||
height: Dimension::Points(self.widget.get_dims().height as f32),
|
height: Dimension::Points(self.widget.get_dims().height as f32),
|
||||||
};
|
};
|
||||||
if scale_factor != 1.0 {
|
|
||||||
if let Dimension::Points(ref mut px) = style.padding.start {
|
|
||||||
*px *= scale_factor;
|
|
||||||
}
|
|
||||||
if let Dimension::Points(ref mut px) = style.padding.end {
|
|
||||||
*px *= scale_factor;
|
|
||||||
}
|
|
||||||
if let Dimension::Points(ref mut px) = style.padding.top {
|
|
||||||
*px *= scale_factor;
|
|
||||||
}
|
|
||||||
if let Dimension::Points(ref mut px) = style.padding.bottom {
|
|
||||||
*px *= scale_factor;
|
|
||||||
}
|
|
||||||
if let Dimension::Points(ref mut px) = style.margin.start {
|
|
||||||
*px *= scale_factor;
|
|
||||||
}
|
|
||||||
if let Dimension::Points(ref mut px) = style.margin.end {
|
|
||||||
*px *= scale_factor;
|
|
||||||
}
|
|
||||||
if let Dimension::Points(ref mut px) = style.margin.top {
|
|
||||||
*px *= scale_factor;
|
|
||||||
}
|
|
||||||
if let Dimension::Points(ref mut px) = style.margin.bottom {
|
|
||||||
*px *= scale_factor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let node = stretch.new_node(style, Vec::new()).unwrap();
|
let node = stretch.new_node(style, Vec::new()).unwrap();
|
||||||
stretch.add_child(parent, node).unwrap();
|
stretch.add_child(parent, node).unwrap();
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
@ -660,12 +622,7 @@ impl Composite {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut nodes = vec![];
|
let mut nodes = vec![];
|
||||||
self.top_level.get_flexbox(
|
self.top_level.get_flexbox(root, &mut stretch, &mut nodes);
|
||||||
root,
|
|
||||||
ctx.get_scale_factor() as f32,
|
|
||||||
&mut stretch,
|
|
||||||
&mut nodes,
|
|
||||||
);
|
|
||||||
nodes.reverse();
|
nodes.reverse();
|
||||||
|
|
||||||
// TODO Express more simply. Constraining this seems useless.
|
// TODO Express more simply. Constraining this seems useless.
|
||||||
@ -682,9 +639,9 @@ impl Composite {
|
|||||||
let result = stretch.layout(root).unwrap();
|
let result = stretch.layout(root).unwrap();
|
||||||
ScreenDims::new(result.size.width.into(), result.size.height.into())
|
ScreenDims::new(result.size.width.into(), result.size.height.into())
|
||||||
};
|
};
|
||||||
let top_left =
|
let top_left = ctx
|
||||||
ctx.canvas
|
.canvas
|
||||||
.align_window(&ctx.prerender.assets, effective_dims, self.horiz, self.vert);
|
.align_window(effective_dims, self.horiz, self.vert);
|
||||||
let offset = self.scroll_offset();
|
let offset = self.scroll_offset();
|
||||||
self.top_level.apply_flexbox(
|
self.top_level.apply_flexbox(
|
||||||
&stretch,
|
&stretch,
|
||||||
@ -792,12 +749,9 @@ impl Composite {
|
|||||||
g.fork_screenspace();
|
g.fork_screenspace();
|
||||||
g.draw_polygon(Color::RED.alpha(0.5), self.top_level.rect.to_polygon());
|
g.draw_polygon(Color::RED.alpha(0.5), self.top_level.rect.to_polygon());
|
||||||
|
|
||||||
let top_left = g.canvas.align_window(
|
let top_left = g
|
||||||
&g.prerender.assets,
|
.canvas
|
||||||
self.container_dims,
|
.align_window(self.container_dims, self.horiz, self.vert);
|
||||||
self.horiz,
|
|
||||||
self.vert,
|
|
||||||
);
|
|
||||||
g.draw_polygon(
|
g.draw_polygon(
|
||||||
Color::BLUE.alpha(0.5),
|
Color::BLUE.alpha(0.5),
|
||||||
Polygon::rectangle(self.container_dims.width, self.container_dims.height)
|
Polygon::rectangle(self.container_dims.width, self.container_dims.height)
|
||||||
@ -1006,9 +960,7 @@ impl CompositeBuilder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If the panel fits without a scrollbar, don't add one.
|
// If the panel fits without a scrollbar, don't add one.
|
||||||
let top_left =
|
let top_left = ctx.canvas.align_window(c.container_dims, c.horiz, c.vert);
|
||||||
ctx.canvas
|
|
||||||
.align_window(&ctx.prerender.assets, c.container_dims, c.horiz, c.vert);
|
|
||||||
if c.contents_dims.width > c.container_dims.width {
|
if c.contents_dims.width > c.container_dims.width {
|
||||||
c.scrollable_x = true;
|
c.scrollable_x = true;
|
||||||
c.top_level = Widget::custom_col(vec![
|
c.top_level = Widget::custom_col(vec![
|
||||||
|
@ -4,7 +4,7 @@ use crate::{Canvas, Event, EventCtx, GfxCtx, Key, Prerender, Style, UpdateType,
|
|||||||
use geom::Duration;
|
use geom::Duration;
|
||||||
use image::{GenericImageView, Pixel};
|
use image::{GenericImageView, Pixel};
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::panic;
|
use std::panic;
|
||||||
use winit::window::Icon;
|
use winit::window::Icon;
|
||||||
|
|
||||||
@ -59,22 +59,16 @@ impl<G: GUI> State<G> {
|
|||||||
|
|
||||||
// Update some ezgui state that's stashed in Canvas for sad reasons.
|
// Update some ezgui state that's stashed in Canvas for sad reasons.
|
||||||
{
|
{
|
||||||
if let Event::WindowResized(width, height) = input.event {
|
if let Event::WindowResized(new_size) = input.event {
|
||||||
let inner_size = prerender.inner.get_inner_size();
|
let inner_size = prerender.window_size();
|
||||||
println!(
|
println!(
|
||||||
"winit event says the window was resized from {}, {} to {}, {}. But inner \
|
"winit event says the window was resized from {}, {} to {:?}. But inner size \
|
||||||
size is {}, {}, so using that",
|
is {:?}, so using that",
|
||||||
self.canvas.window_width,
|
self.canvas.window_width, self.canvas.window_height, new_size, inner_size
|
||||||
self.canvas.window_height,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
inner_size.0,
|
|
||||||
inner_size.1
|
|
||||||
);
|
);
|
||||||
let (width, height) = inner_size;
|
prerender.inner.window_resized(new_size);
|
||||||
prerender.inner.window_resized(width, height);
|
self.canvas.window_width = inner_size.width;
|
||||||
self.canvas.window_width = width;
|
self.canvas.window_height = inner_size.height;
|
||||||
self.canvas.window_height = height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.event == Event::KeyPress(Key::LeftControl) {
|
if input.event == Event::KeyPress(Key::LeftControl) {
|
||||||
@ -197,11 +191,8 @@ impl Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings, make_gui: F) -> ! {
|
pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings, make_gui: F) -> ! {
|
||||||
let (prerender_innards, event_loop, window_size) =
|
let (prerender_innards, event_loop) = crate::backend::setup(&settings.window_title);
|
||||||
crate::backend::setup(&settings.window_title);
|
|
||||||
|
|
||||||
let mut canvas = Canvas::new(window_size.width, window_size.height);
|
|
||||||
prerender_innards.window_resized(canvas.window_width, canvas.window_height);
|
|
||||||
if let Some(ref path) = settings.window_icon {
|
if let Some(ref path) = settings.window_icon {
|
||||||
if !cfg!(target_arch = "wasm32") {
|
if !cfg!(target_arch = "wasm32") {
|
||||||
let image = image::open(path).unwrap();
|
let image = image::open(path).unwrap();
|
||||||
@ -214,18 +205,24 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
|||||||
prerender_innards.set_window_icon(icon);
|
prerender_innards.set_window_icon(icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let monitor_scale_factor = prerender_innards.monitor_scale_factor();
|
||||||
let prerender = Prerender {
|
let prerender = Prerender {
|
||||||
assets: Assets::new(
|
assets: Assets::new(abstutil::path("system/fonts")),
|
||||||
abstutil::path("system/fonts"),
|
|
||||||
settings
|
|
||||||
.scale_factor
|
|
||||||
.unwrap_or_else(|| prerender_innards.monitor_scale_factor()),
|
|
||||||
),
|
|
||||||
num_uploads: Cell::new(0),
|
num_uploads: Cell::new(0),
|
||||||
inner: prerender_innards,
|
inner: prerender_innards,
|
||||||
|
scale_factor: RefCell::new(settings.scale_factor.unwrap_or(monitor_scale_factor)),
|
||||||
};
|
};
|
||||||
let mut style = Style::standard();
|
let mut style = Style::standard();
|
||||||
|
|
||||||
|
// DPI TODO: This is going to cause a regression for devs on linux - since not all UI elements
|
||||||
|
// properly resize, e.g. the minimap. However, since only dev's are launching directly to the
|
||||||
|
// simulation, in practice this shouldn't cause much of an issue until we can get the minimap
|
||||||
|
// to resize itself.
|
||||||
|
let initial_size = prerender.window_size();
|
||||||
|
let mut canvas = Canvas::new(initial_size);
|
||||||
|
prerender.inner.window_resized(initial_size);
|
||||||
|
|
||||||
let gui = make_gui(&mut EventCtx {
|
let gui = make_gui(&mut EventCtx {
|
||||||
fake_mouseover: true,
|
fake_mouseover: true,
|
||||||
input: UserInput::new(Event::NoOp, &canvas),
|
input: UserInput::new(Event::NoOp, &canvas),
|
||||||
@ -265,7 +262,8 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
|
|||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
winit::event::Event::WindowEvent { event, .. } => {
|
winit::event::Event::WindowEvent { event, .. } => {
|
||||||
if let Some(ev) = Event::from_winit_event(event) {
|
let scale_factor = prerender.get_scale_factor();
|
||||||
|
if let Some(ev) = Event::from_winit_event(event, scale_factor) {
|
||||||
ev
|
ev
|
||||||
} else {
|
} else {
|
||||||
// Don't touch control_flow if we got an irrelevant event
|
// Don't touch control_flow if we got an irrelevant event
|
||||||
|
@ -2,6 +2,7 @@ use crate::Canvas;
|
|||||||
use geom::{trim_f64, Polygon, Pt2D};
|
use geom::{trim_f64, Polygon, Pt2D};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// ScreenPt is in units of logical pixels, as opposed to physical pixels.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct ScreenPt {
|
pub struct ScreenPt {
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
@ -20,6 +21,13 @@ impl ScreenPt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<winit::dpi::LogicalPosition<f64>> for ScreenPt {
|
||||||
|
fn from(lp: winit::dpi::LogicalPosition<f64>) -> ScreenPt {
|
||||||
|
ScreenPt { x: lp.x, y: lp.y }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ScreenRectangle is in units of logical pixels, as opposed to physical pixels.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ScreenRectangle {
|
pub struct ScreenRectangle {
|
||||||
pub x1: f64,
|
pub x1: f64,
|
||||||
@ -87,7 +95,7 @@ impl ScreenRectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Everything screen-space should probably just be usize, can't have fractional pixels?
|
/// ScreenDims is in units of logical pixels, as opposed to physical pixels.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct ScreenDims {
|
pub struct ScreenDims {
|
||||||
pub width: f64,
|
pub width: f64,
|
||||||
@ -125,3 +133,12 @@ impl ScreenDims {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<winit::dpi::LogicalSize<f64>> for ScreenDims {
|
||||||
|
fn from(lp: winit::dpi::LogicalSize<f64>) -> ScreenDims {
|
||||||
|
ScreenDims {
|
||||||
|
width: lp.width,
|
||||||
|
height: lp.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -12,8 +12,8 @@ pub const LOW_QUALITY: f32 = 1.0;
|
|||||||
// Code here adapted from
|
// Code here adapted from
|
||||||
// https://github.com/nical/lyon/blob/0d0ee771180fb317b986d9cf30266722e0773e01/examples/wgpu_svg/src/main.rs
|
// https://github.com/nical/lyon/blob/0d0ee771180fb317b986d9cf30266722e0773e01/examples/wgpu_svg/src/main.rs
|
||||||
|
|
||||||
pub fn load_svg(prerender: &Prerender, filename: &str, scale_factor: f64) -> (GeomBatch, Bounds) {
|
pub fn load_svg(prerender: &Prerender, filename: &str) -> (GeomBatch, Bounds) {
|
||||||
if let Some(pair) = prerender.assets.get_cached_svg(filename, scale_factor) {
|
if let Some(pair) = prerender.assets.get_cached_svg(filename) {
|
||||||
return pair;
|
return pair;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,14 +25,11 @@ pub fn load_svg(prerender: &Prerender, filename: &str, scale_factor: f64) -> (Ge
|
|||||||
};
|
};
|
||||||
let svg_tree = usvg::Tree::from_data(&raw, &usvg::Options::default()).unwrap();
|
let svg_tree = usvg::Tree::from_data(&raw, &usvg::Options::default()).unwrap();
|
||||||
let mut batch = GeomBatch::new();
|
let mut batch = GeomBatch::new();
|
||||||
match add_svg_inner(&mut batch, svg_tree, HIGH_QUALITY, scale_factor) {
|
match add_svg_inner(&mut batch, svg_tree, HIGH_QUALITY) {
|
||||||
Ok(bounds) => {
|
Ok(bounds) => {
|
||||||
prerender.assets.cache_svg(
|
prerender
|
||||||
filename.to_string(),
|
.assets
|
||||||
scale_factor,
|
.cache_svg(filename.to_string(), batch.clone(), bounds.clone());
|
||||||
batch.clone(),
|
|
||||||
bounds.clone(),
|
|
||||||
);
|
|
||||||
(batch, bounds)
|
(batch, bounds)
|
||||||
}
|
}
|
||||||
Err(err) => panic!("{}: {}", filename, err),
|
Err(err) => panic!("{}: {}", filename, err),
|
||||||
@ -46,7 +43,6 @@ pub fn add_svg_inner(
|
|||||||
batch: &mut GeomBatch,
|
batch: &mut GeomBatch,
|
||||||
svg_tree: usvg::Tree,
|
svg_tree: usvg::Tree,
|
||||||
tolerance: f32,
|
tolerance: f32,
|
||||||
scale: f64,
|
|
||||||
) -> Result<Bounds, String> {
|
) -> Result<Bounds, String> {
|
||||||
let mut fill_tess = tessellation::FillTessellator::new();
|
let mut fill_tess = tessellation::FillTessellator::new();
|
||||||
let mut stroke_tess = tessellation::StrokeTessellator::new();
|
let mut stroke_tess = tessellation::StrokeTessellator::new();
|
||||||
@ -88,7 +84,7 @@ pub fn add_svg_inner(
|
|||||||
Polygon::precomputed(
|
Polygon::precomputed(
|
||||||
mesh.vertices
|
mesh.vertices
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| Pt2D::new(scale * f64::from(v.x), scale * f64::from(v.y)))
|
.map(|v| Pt2D::new(f64::from(v.x), f64::from(v.y)))
|
||||||
.collect(),
|
.collect(),
|
||||||
mesh.indices.into_iter().map(|idx| idx as usize).collect(),
|
mesh.indices.into_iter().map(|idx| idx as usize).collect(),
|
||||||
),
|
),
|
||||||
@ -97,7 +93,7 @@ pub fn add_svg_inner(
|
|||||||
let size = svg_tree.svg_node().size;
|
let size = svg_tree.svg_node().size;
|
||||||
Ok(Bounds::from(&vec![
|
Ok(Bounds::from(&vec![
|
||||||
Pt2D::new(0.0, 0.0),
|
Pt2D::new(0.0, 0.0),
|
||||||
Pt2D::new(scale * size.width(), scale * size.height()),
|
Pt2D::new(size.width(), size.height()),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,12 +454,7 @@ fn render_line(spans: Vec<TextSpan>, tolerance: f32, assets: &Assets) -> GeomBat
|
|||||||
Err(err) => panic!("render_line({}): {}", contents, err),
|
Err(err) => panic!("render_line({}): {}", contents, err),
|
||||||
};
|
};
|
||||||
let mut batch = GeomBatch::new();
|
let mut batch = GeomBatch::new();
|
||||||
match crate::svg::add_svg_inner(
|
match crate::svg::add_svg_inner(&mut batch, svg_tree, tolerance) {
|
||||||
&mut batch,
|
|
||||||
svg_tree,
|
|
||||||
tolerance,
|
|
||||||
*assets.scale_factor.borrow(),
|
|
||||||
) {
|
|
||||||
Ok(_) => batch,
|
Ok(_) => batch,
|
||||||
Err(err) => panic!("render_line({}): {}", contents, err),
|
Err(err) => panic!("render_line({}): {}", contents, err),
|
||||||
}
|
}
|
||||||
|
@ -255,11 +255,7 @@ impl BtnBuilder {
|
|||||||
rewrite_hover,
|
rewrite_hover,
|
||||||
maybe_tooltip,
|
maybe_tooltip,
|
||||||
} => {
|
} => {
|
||||||
let (normal, bounds) = svg::load_svg(
|
let (normal, bounds) = svg::load_svg(ctx.prerender, &path);
|
||||||
ctx.prerender,
|
|
||||||
&path,
|
|
||||||
*ctx.prerender.assets.scale_factor.borrow(),
|
|
||||||
);
|
|
||||||
let geom = Polygon::rectangle(bounds.width(), bounds.height());
|
let geom = Polygon::rectangle(bounds.width(), bounds.height());
|
||||||
|
|
||||||
let hovered = normal.clone().color(rewrite_hover);
|
let hovered = normal.clone().color(rewrite_hover);
|
||||||
|
@ -22,11 +22,7 @@ impl JustDraw {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn svg(ctx: &EventCtx, filename: String) -> Widget {
|
pub fn svg(ctx: &EventCtx, filename: String) -> Widget {
|
||||||
let (batch, bounds) = svg::load_svg(
|
let (batch, bounds) = svg::load_svg(ctx.prerender, &filename);
|
||||||
ctx.prerender,
|
|
||||||
&filename,
|
|
||||||
*ctx.prerender.assets.scale_factor.borrow(),
|
|
||||||
);
|
|
||||||
// TODO The dims will be wrong; it'll only look at geometry, not the padding in the image.
|
// TODO The dims will be wrong; it'll only look at geometry, not the padding in the image.
|
||||||
Widget::new(Box::new(JustDraw {
|
Widget::new(Box::new(JustDraw {
|
||||||
dims: ScreenDims::new(bounds.width(), bounds.height()),
|
dims: ScreenDims::new(bounds.width(), bounds.height()),
|
||||||
@ -35,11 +31,7 @@ impl JustDraw {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
pub fn svg_transform(ctx: &EventCtx, filename: &str, rewrite: RewriteColor) -> Widget {
|
pub fn svg_transform(ctx: &EventCtx, filename: &str, rewrite: RewriteColor) -> Widget {
|
||||||
let (batch, bounds) = svg::load_svg(
|
let (batch, bounds) = svg::load_svg(ctx.prerender, filename);
|
||||||
ctx.prerender,
|
|
||||||
filename,
|
|
||||||
*ctx.prerender.assets.scale_factor.borrow(),
|
|
||||||
);
|
|
||||||
let batch = batch.color(rewrite);
|
let batch = batch.color(rewrite);
|
||||||
// TODO The dims will be wrong; it'll only look at geometry, not the padding in the image.
|
// TODO The dims will be wrong; it'll only look at geometry, not the padding in the image.
|
||||||
Widget::new(Box::new(JustDraw {
|
Widget::new(Box::new(JustDraw {
|
||||||
|
@ -36,9 +36,9 @@ impl CityPicker {
|
|||||||
&mut abstutil::Timer::throwaway(),
|
&mut abstutil::Timer::throwaway(),
|
||||||
) {
|
) {
|
||||||
let bounds = city.boundary.get_bounds();
|
let bounds = city.boundary.get_bounds();
|
||||||
let zoom_no_scale_factor = (0.8 * ctx.canvas.window_width / bounds.width())
|
|
||||||
|
let zoom = (0.8 * ctx.canvas.window_width / bounds.width())
|
||||||
.min(0.8 * ctx.canvas.window_height / bounds.height());
|
.min(0.8 * ctx.canvas.window_height / bounds.height());
|
||||||
let zoom = zoom_no_scale_factor / ctx.get_scale_factor();
|
|
||||||
|
|
||||||
batch.push(app.cs.map_background, city.boundary);
|
batch.push(app.cs.map_background, city.boundary);
|
||||||
for (area_type, polygon) in city.areas {
|
for (area_type, polygon) in city.areas {
|
||||||
@ -56,7 +56,7 @@ impl CityPicker {
|
|||||||
} else {
|
} else {
|
||||||
batch.push(color, polygon.to_outline(Distance::meters(200.0)).unwrap());
|
batch.push(color, polygon.to_outline(Distance::meters(200.0)).unwrap());
|
||||||
}
|
}
|
||||||
regions.push((name, color, polygon.scale(zoom_no_scale_factor)));
|
regions.push((name, color, polygon.scale(zoom)));
|
||||||
}
|
}
|
||||||
batch = batch.scale(zoom);
|
batch = batch.scale(zoom);
|
||||||
}
|
}
|
||||||
|
@ -932,9 +932,7 @@ fn make_signal_diagram(
|
|||||||
ctx,
|
ctx,
|
||||||
GeomBatch::from(vec![(
|
GeomBatch::from(vec![(
|
||||||
Color::WHITE,
|
Color::WHITE,
|
||||||
// TODO draw_batch will scale up, but that's inappropriate here, since we're
|
Polygon::rectangle(0.2 * ctx.canvas.window_width, 2.0),
|
||||||
// depending on window width, which already factors in scale
|
|
||||||
Polygon::rectangle(0.2 * ctx.canvas.window_width / ctx.get_scale_factor(), 2.0),
|
|
||||||
)]),
|
)]),
|
||||||
)
|
)
|
||||||
.centered_horiz(),
|
.centered_horiz(),
|
||||||
@ -1029,7 +1027,7 @@ fn make_signal_diagram(
|
|||||||
ctx,
|
ctx,
|
||||||
GeomBatch::from(vec![(
|
GeomBatch::from(vec![(
|
||||||
Color::WHITE,
|
Color::WHITE,
|
||||||
Polygon::rectangle(0.2 * ctx.canvas.window_width / ctx.get_scale_factor(), 2.0),
|
Polygon::rectangle(0.2 * ctx.canvas.window_width, 2.0),
|
||||||
)]),
|
)]),
|
||||||
)
|
)
|
||||||
.centered_horiz(),
|
.centered_horiz(),
|
||||||
|
@ -167,7 +167,7 @@ pub fn current_demand(
|
|||||||
txt_batch.append(
|
txt_batch.append(
|
||||||
Text::from(Line(prettyprint_usize(demand)).fg(Color::RED))
|
Text::from(Line(prettyprint_usize(demand)).fg(Color::RED))
|
||||||
.render_ctx(ctx)
|
.render_ctx(ctx)
|
||||||
.scale(0.15 / ctx.get_scale_factor())
|
.scale(0.15)
|
||||||
.centered_on(pl.middle()),
|
.centered_on(pl.middle()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -496,12 +496,8 @@ fn make_timeline(
|
|||||||
(100.0 * percent_duration) as usize
|
(100.0 * percent_duration) as usize
|
||||||
)));
|
)));
|
||||||
|
|
||||||
// TODO We're manually mixing screenspace_svg, our own geometry, and a centered_on(). Be
|
|
||||||
// careful about the scale factor. I'm confused about some of the math here, figured it out
|
|
||||||
// by trial and error.
|
|
||||||
let scale = ctx.get_scale_factor();
|
|
||||||
let phase_width = total_width * percent_duration;
|
let phase_width = total_width * percent_duration;
|
||||||
let rect = Polygon::rectangle(phase_width * scale, 15.0 * scale);
|
let rect = Polygon::rectangle(phase_width, 15.0);
|
||||||
let mut normal = GeomBatch::from(vec![(color, rect.clone())]);
|
let mut normal = GeomBatch::from(vec![(color, rect.clone())]);
|
||||||
if idx == num_phases - 1 {
|
if idx == num_phases - 1 {
|
||||||
if let Some(p) = progress_along_path {
|
if let Some(p) = progress_along_path {
|
||||||
@ -510,7 +506,7 @@ fn make_timeline(
|
|||||||
ctx.prerender,
|
ctx.prerender,
|
||||||
"system/assets/timeline/current_pos.svg",
|
"system/assets/timeline/current_pos.svg",
|
||||||
)
|
)
|
||||||
.centered_on(Pt2D::new(p * phase_width * scale, 7.5 * scale)),
|
.centered_on(Pt2D::new(p * phase_width, 7.5)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -534,7 +530,7 @@ fn make_timeline(
|
|||||||
)
|
)
|
||||||
.centered_on(
|
.centered_on(
|
||||||
// TODO Hardcoded layouting...
|
// TODO Hardcoded layouting...
|
||||||
Pt2D::new(0.5 * phase_width * scale, -20.0),
|
Pt2D::new(0.5 * phase_width, -20.0),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -145,26 +145,6 @@ impl OptionsPanel {
|
|||||||
ColorSchemeChoice::choices(),
|
ColorSchemeChoice::choices(),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
Widget::row(vec![
|
|
||||||
format!(
|
|
||||||
"Scale factor for text / UI elements (your monitor is {}):",
|
|
||||||
ctx.monitor_scale_factor()
|
|
||||||
)
|
|
||||||
.draw_text(ctx),
|
|
||||||
Widget::dropdown(ctx, "Scale factor", ctx.get_scale_factor(), {
|
|
||||||
let mut choices = vec![
|
|
||||||
Choice::new("0.5", 0.5),
|
|
||||||
Choice::new("1.0", 1.0),
|
|
||||||
Choice::new("1.5", 1.5),
|
|
||||||
Choice::new("2.0", 2.0),
|
|
||||||
];
|
|
||||||
let native = ctx.monitor_scale_factor();
|
|
||||||
if !choices.iter().any(|c| c.data == native) {
|
|
||||||
choices.push(Choice::new(native.to_string(), native));
|
|
||||||
}
|
|
||||||
choices
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
"Camera zoom to switch to unzoomed view".draw_text(ctx),
|
"Camera zoom to switch to unzoomed view".draw_text(ctx),
|
||||||
Widget::dropdown(
|
Widget::dropdown(
|
||||||
@ -253,11 +233,6 @@ impl State for OptionsPanel {
|
|||||||
app.switch_map(ctx, app.primary.current_flags.sim_flags.load.clone());
|
app.switch_map(ctx, app.primary.current_flags.sim_flags.load.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let factor = self.composite.dropdown_value("Scale factor");
|
|
||||||
if ctx.get_scale_factor() != factor {
|
|
||||||
ctx.set_scale_factor(factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.opts.min_zoom_for_detail = self.composite.dropdown_value("min zoom");
|
app.opts.min_zoom_for_detail = self.composite.dropdown_value("min zoom");
|
||||||
app.opts.large_unzoomed_agents =
|
app.opts.large_unzoomed_agents =
|
||||||
self.composite.is_checked("Draw enlarged unzoomed agents");
|
self.composite.is_checked("Draw enlarged unzoomed agents");
|
||||||
|
@ -180,7 +180,7 @@ impl Demand {
|
|||||||
txt_batch.append(
|
txt_batch.append(
|
||||||
Text::from(Line(prettyprint_usize(demand)).fg(Color::RED))
|
Text::from(Line(prettyprint_usize(demand)).fg(Color::RED))
|
||||||
.render_ctx(ctx)
|
.render_ctx(ctx)
|
||||||
.scale(0.15 / ctx.get_scale_factor())
|
.scale(0.15)
|
||||||
.centered_on(pl.middle()),
|
.centered_on(pl.middle()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -448,12 +448,10 @@ pub fn make_table(
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(idx, w)| {
|
.map(|(idx, w)| {
|
||||||
let margin = extra_margin + width_per_col[idx] - w.get_width_for_forcing();
|
let margin = extra_margin + width_per_col[idx] - w.get_width_for_forcing();
|
||||||
// TODO margin_right scales up, so we have to cancel that out. Otherwise here we're
|
|
||||||
// already working in physical pixels. Sigh.
|
|
||||||
if idx == width_per_col.len() - 1 {
|
if idx == width_per_col.len() - 1 {
|
||||||
w.margin_right(((margin - extra_margin) / ctx.get_scale_factor()) as usize)
|
w.margin_right((margin - extra_margin) as usize)
|
||||||
} else {
|
} else {
|
||||||
w.margin_right((margin / ctx.get_scale_factor()) as usize)
|
w.margin_right(margin as usize)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -248,7 +248,7 @@ fn make_meter(
|
|||||||
ctx,
|
ctx,
|
||||||
GeomBatch::from(vec![(
|
GeomBatch::from(vec![(
|
||||||
Color::WHITE,
|
Color::WHITE,
|
||||||
Polygon::rectangle(0.2 * ctx.canvas.window_width / ctx.get_scale_factor(), 2.0),
|
Polygon::rectangle(0.2 * ctx.canvas.window_width, 2.0),
|
||||||
)]),
|
)]),
|
||||||
)
|
)
|
||||||
.centered_horiz(),
|
.centered_horiz(),
|
||||||
|
@ -293,7 +293,7 @@ fn make_meter(
|
|||||||
ctx,
|
ctx,
|
||||||
GeomBatch::from(vec![(
|
GeomBatch::from(vec![(
|
||||||
Color::WHITE,
|
Color::WHITE,
|
||||||
Polygon::rectangle(0.2 * ctx.canvas.window_width / ctx.get_scale_factor(), 2.0),
|
Polygon::rectangle(0.2 * ctx.canvas.window_width, 2.0),
|
||||||
)]),
|
)]),
|
||||||
)
|
)
|
||||||
.centered_horiz(),
|
.centered_horiz(),
|
||||||
|
@ -330,7 +330,7 @@ impl AgentMeter {
|
|||||||
ctx,
|
ctx,
|
||||||
GeomBatch::from(vec![(
|
GeomBatch::from(vec![(
|
||||||
Color::WHITE,
|
Color::WHITE,
|
||||||
Polygon::rectangle(0.2 * ctx.canvas.window_width / ctx.get_scale_factor(), 2.0),
|
Polygon::rectangle(0.2 * ctx.canvas.window_width, 2.0),
|
||||||
)]),
|
)]),
|
||||||
)
|
)
|
||||||
.centered_horiz(),
|
.centered_horiz(),
|
||||||
|
Loading…
Reference in New Issue
Block a user