From 81c5ae370073fa1ea27d9832b2bea08a657aee0f Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 20 Mar 2020 18:05:50 -0700 Subject: [PATCH] consolidate ezgui internals a little, and start a little bit of rustdoc --- ezgui/Cargo.toml | 1 + ezgui/README.md | 2 +- ezgui/examples/demo.rs | 5 +- ezgui/src/drawing.rs | 161 +---------------------------- ezgui/src/geom.rs | 178 ++++++++++++++++++++++++++++++++ ezgui/src/layout.rs | 52 ---------- ezgui/src/lib.rs | 23 +++-- ezgui/src/managed.rs | 9 +- ezgui/src/runner.rs | 7 +- ezgui/src/widgets/button.rs | 2 +- ezgui/src/widgets/checkbox.rs | 4 +- ezgui/src/widgets/dropdown.rs | 9 +- ezgui/src/widgets/filler.rs | 4 +- ezgui/src/widgets/histogram.rs | 2 +- ezgui/src/widgets/mod.rs | 95 +++++++++++------ ezgui/src/widgets/modal_menu.rs | 24 ++--- ezgui/src/widgets/no_op.rs | 4 +- ezgui/src/widgets/plot.rs | 2 +- ezgui/src/widgets/popup_menu.rs | 2 +- ezgui/src/widgets/slider.rs | 4 +- ezgui/src/widgets/text_box.rs | 2 +- ezgui/src/widgets/wizard.rs | 3 +- 22 files changed, 304 insertions(+), 291 deletions(-) create mode 100644 ezgui/src/geom.rs delete mode 100644 ezgui/src/layout.rs diff --git a/ezgui/Cargo.toml b/ezgui/Cargo.toml index dee3e8053c..01bad22b4e 100644 --- a/ezgui/Cargo.toml +++ b/ezgui/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Dustin Carlino "] edition = "2018" [features] +default = ["glium-backend"] glium-backend = ["glium", "glutin", "usvg/text"] glow-backend = ["glow", "glutin", "usvg/text"] wasm-backend = ["glow/stdweb", "instant/stdweb", "stdweb", "webgl_stdweb", "winit/stdweb"] diff --git a/ezgui/README.md b/ezgui/README.md index 4b4c327a89..1736de5451 100644 --- a/ezgui/README.md +++ b/ezgui/README.md @@ -10,7 +10,7 @@ considering cleaning up the API surface and making it a proper standalone crate. ``` git clone https://github.com/dabreegster/abstreet.git cd abstreet/ezgui -cargo run --example demo --features glium-backend +cargo run --example demo ``` ![demo](demo.gif) diff --git a/ezgui/examples/demo.rs b/ezgui/examples/demo.rs index 9c78877492..86870fe20b 100644 --- a/ezgui/examples/demo.rs +++ b/ezgui/examples/demo.rs @@ -2,10 +2,11 @@ // images. // // To run: -// > cargo run --example demo --features glium-backend +// > cargo run --example demo // // Try the web version, but there's no text rendering yet: -// > cargo web start --target wasm32-unknown-unknown --features wasm-backend --example demo +// > cargo web start --target wasm32-unknown-unknown --no-default-features \ +// --features wasm-backend --example demo use ezgui::{ hotkey, lctrl, Btn, Color, Composite, Drawable, EventCtx, EventLoopMode, GeomBatch, GfxCtx, diff --git a/ezgui/src/drawing.rs b/ezgui/src/drawing.rs index 5c5d008890..1c7d315894 100644 --- a/ezgui/src/drawing.rs +++ b/ezgui/src/drawing.rs @@ -1,11 +1,10 @@ use crate::assets::Assets; use crate::backend::{GfxCtxInnards, PrerenderInnards}; -use crate::svg; use crate::{ - Canvas, Color, Drawable, EventCtx, HorizontalAlignment, ScreenDims, ScreenPt, ScreenRectangle, + Canvas, Color, Drawable, GeomBatch, HorizontalAlignment, ScreenDims, ScreenPt, ScreenRectangle, Text, VerticalAlignment, }; -use geom::{Angle, Bounds, Circle, Distance, Line, Polygon, Pt2D}; +use geom::{Bounds, Circle, Distance, Line, Polygon, Pt2D}; use std::cell::Cell; // Lower is more on top @@ -286,162 +285,6 @@ impl<'a> GfxCtx<'a> { } } -#[derive(Clone)] -pub struct GeomBatch { - list: Vec<(Color, Polygon)>, - // TODO A weird hack for text. - pub(crate) dims_text: bool, -} - -impl GeomBatch { - pub fn new() -> GeomBatch { - GeomBatch { - list: Vec::new(), - dims_text: false, - } - } - - pub fn from(list: Vec<(Color, Polygon)>) -> GeomBatch { - GeomBatch { - list, - dims_text: false, - } - } - - pub fn push(&mut self, color: Color, p: Polygon) { - self.list.push((color, p)); - } - - pub fn extend(&mut self, color: Color, polys: Vec) { - for p in polys { - self.list.push((color, p)); - } - } - - pub fn append(&mut self, other: GeomBatch) { - self.list.extend(other.list); - } - - pub fn consume(self) -> Vec<(Color, Polygon)> { - self.list - } - - pub fn draw(self, g: &mut GfxCtx) { - let refs = self.list.iter().map(|(color, p)| (*color, p)).collect(); - let obj = g.prerender.upload_temporary(refs); - g.redraw(&obj); - } - - pub fn upload(self, ctx: &EventCtx) -> Drawable { - ctx.prerender.upload(self) - } - - // Sets the top-left to 0, 0. Not sure exactly when this should be used. - pub(crate) fn autocrop(mut self) -> GeomBatch { - let mut bounds = Bounds::new(); - for (_, poly) in &self.list { - bounds.union(poly.get_bounds()); - } - if bounds.min_x == 0.0 && bounds.min_y == 0.0 { - return self; - } - for (_, poly) in &mut self.list { - *poly = poly.translate(-bounds.min_x, -bounds.min_y); - } - self - } - - pub(crate) fn is_empty(&self) -> bool { - self.list.is_empty() - } - - pub fn get_dims(&self) -> ScreenDims { - // TODO Maybe warn about this happening and avoid in the first place? Sometimes we wind up - // trying to draw completely empty text. - if self.is_empty() { - return ScreenDims::new(0.0, 0.0); - } - let mut bounds = Bounds::new(); - for (_, poly) in &self.list { - bounds.union(poly.get_bounds()); - } - if self.dims_text { - ScreenDims::new(bounds.max_x, bounds.max_y) - } else { - ScreenDims::new(bounds.width(), bounds.height()) - } - } - - // Slightly weird use case, but hotswap colors. - pub fn rewrite_color(&mut self, transformation: RewriteColor) { - for (c, _) in self.list.iter_mut() { - match transformation { - RewriteColor::NoOp => {} - RewriteColor::Change(from, to) => { - if *c == from { - *c = to; - } - } - RewriteColor::ChangeAll(to) => { - *c = to; - } - } - } - } - - pub fn from_svg>( - ctx: &EventCtx, - path: I, - rewrite: RewriteColor, - ) -> (GeomBatch, Bounds) { - let (mut batch, bounds) = svg::load_svg(ctx.prerender, &path.into()); - batch.rewrite_color(rewrite); - (batch, bounds) - } - - // TODO Weird API... - pub fn add_svg( - &mut self, - prerender: &Prerender, - filename: &str, - center: Pt2D, - scale: f64, - rotate: Angle, - ) { - self.add_transformed(svg::load_svg(prerender, filename).0, center, scale, rotate); - } - - // This centers on the pt! - pub fn add_transformed(&mut self, other: GeomBatch, center: Pt2D, scale: f64, rotate: Angle) { - let dims = other.get_dims(); - let dx = center.x() - dims.width * scale / 2.0; - let dy = center.y() - dims.height * scale / 2.0; - for (color, mut poly) in other.consume() { - // Avoid unnecessary transformations for slight perf boost - if scale != 1.0 { - poly = poly.scale(scale); - } - poly = poly.translate(dx, dy); - if rotate != Angle::ZERO { - poly = poly.rotate(rotate); - } - self.push(color, poly); - } - } - - pub fn add_translated(&mut self, other: GeomBatch, dx: f64, dy: f64) { - for (color, poly) in other.consume() { - self.push(color, poly.translate(dx, dy)); - } - } -} - -pub enum RewriteColor { - NoOp, - Change(Color, Color), - ChangeAll(Color), -} - // TODO Don't expose this directly // TODO Rename or something maybe. This actually owns all the permanent state of everything. pub struct Prerender { diff --git a/ezgui/src/geom.rs b/ezgui/src/geom.rs new file mode 100644 index 0000000000..95bd1cd947 --- /dev/null +++ b/ezgui/src/geom.rs @@ -0,0 +1,178 @@ +use crate::{svg, Color, Drawable, EventCtx, GfxCtx, Prerender, ScreenDims}; +use geom::{Angle, Bounds, Polygon, Pt2D}; + +/// A mutable builder for a group of colored polygons. +#[derive(Clone)] +pub struct GeomBatch { + pub(crate) list: Vec<(Color, Polygon)>, + // TODO A weird hack for text. + pub(crate) dims_text: bool, +} + +impl GeomBatch { + /// Creates an empty batch. + pub fn new() -> GeomBatch { + GeomBatch { + list: Vec::new(), + dims_text: false, + } + } + + /// Creates a batch of colored polygons. + pub fn from(list: Vec<(Color, Polygon)>) -> GeomBatch { + GeomBatch { + list, + dims_text: false, + } + } + + /// Adds a single colored polygon. + pub fn push(&mut self, color: Color, p: Polygon) { + self.list.push((color, p)); + } + + /// Applies one color to many polygons. + pub fn extend(&mut self, color: Color, polys: Vec) { + for p in polys { + self.list.push((color, p)); + } + } + + /// Appends all colored polygons from another batch to the current one. + pub fn append(&mut self, other: GeomBatch) { + self.list.extend(other.list); + } + + /// Returns the colored polygons in this batch, destroying the batch. + pub fn consume(self) -> Vec<(Color, Polygon)> { + self.list + } + + /// Draws the batch, consuming it. Only use this for drawing things once. + pub fn draw(self, g: &mut GfxCtx) { + let refs = self.list.iter().map(|(color, p)| (*color, p)).collect(); + let obj = g.prerender.upload_temporary(refs); + g.redraw(&obj); + } + + /// Upload the batch of polygons to the GPU, returning something that can be cheaply redrawn + /// many times later. + pub fn upload(self, ctx: &EventCtx) -> Drawable { + ctx.prerender.upload(self) + } + + /// Sets the top-left to 0, 0. Not sure exactly when this should be used. + pub(crate) fn autocrop(mut self) -> GeomBatch { + let mut bounds = Bounds::new(); + for (_, poly) in &self.list { + bounds.union(poly.get_bounds()); + } + if bounds.min_x == 0.0 && bounds.min_y == 0.0 { + return self; + } + for (_, poly) in &mut self.list { + *poly = poly.translate(-bounds.min_x, -bounds.min_y); + } + self + } + + /// True when the batch is empty. + pub(crate) fn is_empty(&self) -> bool { + self.list.is_empty() + } + + /// Returns the width and height of all geometry contained in the batch. + pub fn get_dims(&self) -> ScreenDims { + // TODO Maybe warn about this happening and avoid in the first place? Sometimes we wind up + // trying to draw completely empty text. + if self.is_empty() { + return ScreenDims::new(0.0, 0.0); + } + let mut bounds = Bounds::new(); + for (_, poly) in &self.list { + bounds.union(poly.get_bounds()); + } + if self.dims_text { + ScreenDims::new(bounds.max_x, bounds.max_y) + } else { + ScreenDims::new(bounds.width(), bounds.height()) + } + } + + /// Transforms all colors in a batch. + pub fn rewrite_color(&mut self, transformation: RewriteColor) { + for (c, _) in self.list.iter_mut() { + match transformation { + RewriteColor::NoOp => {} + RewriteColor::Change(from, to) => { + if *c == from { + *c = to; + } + } + RewriteColor::ChangeAll(to) => { + *c = to; + } + } + } + } + + // TODO Weird API. + /// Creates a new batch containing an SVG image, also returning the bounds of the SVG. The + /// dimensions come from the SVG image size -- if the image has blank padding on the right and + /// bottom side, this is captured by the bounds. + pub fn from_svg>( + ctx: &EventCtx, + path: I, + rewrite: RewriteColor, + ) -> (GeomBatch, Bounds) { + let (mut batch, bounds) = svg::load_svg(ctx.prerender, &path.into()); + batch.rewrite_color(rewrite); + (batch, bounds) + } + + // TODO Weird API. + /// Adds an SVG image to the current batch, applying the transformations first. + pub fn add_svg( + &mut self, + prerender: &Prerender, + filename: &str, + center: Pt2D, + scale: f64, + rotate: Angle, + ) { + self.add_transformed(svg::load_svg(prerender, filename).0, center, scale, rotate); + } + + /// Adds geometry from another batch to the current batch, first transforming it. The + /// translation centers on the given point. + pub fn add_transformed(&mut self, other: GeomBatch, center: Pt2D, scale: f64, rotate: Angle) { + let dims = other.get_dims(); + let dx = center.x() - dims.width * scale / 2.0; + let dy = center.y() - dims.height * scale / 2.0; + for (color, mut poly) in other.consume() { + // Avoid unnecessary transformations for slight perf boost + if scale != 1.0 { + poly = poly.scale(scale); + } + poly = poly.translate(dx, dy); + if rotate != Angle::ZERO { + poly = poly.rotate(rotate); + } + self.push(color, poly); + } + } + + // TODO Weird API + /// Adds geometry from another batch to the current batch, first translating it. + pub fn add_translated(&mut self, other: GeomBatch, dx: f64, dy: f64) { + for (color, poly) in other.consume() { + self.push(color, poly.translate(dx, dy)); + } + } +} + +pub enum RewriteColor { + NoOp, + Change(Color, Color), + ChangeAll(Color), +} diff --git a/ezgui/src/layout.rs b/ezgui/src/layout.rs deleted file mode 100644 index dcee0bf6a4..0000000000 --- a/ezgui/src/layout.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::{EventCtx, ScreenDims, ScreenPt}; -use ordered_float::NotNan; - -// TODO Move this to widgets/mod - -pub trait Widget { - fn get_dims(&self) -> ScreenDims; - fn set_pos(&mut self, top_left: ScreenPt); -} - -#[derive(Clone, Copy)] -pub enum ContainerOrientation { - TopLeft, - TopRight, - TopLeftButDownABit(f64), - Centered, - // Place the widget this percentage along the width of the screen - Top(f64), -} - -pub fn stack_vertically( - orientation: ContainerOrientation, - ctx: &EventCtx, - widgets: Vec<&mut dyn Widget>, -) { - assert!(!widgets.is_empty()); - - let dims_per_widget: Vec = widgets.iter().map(|w| w.get_dims()).collect(); - let total_width = dims_per_widget - .iter() - .map(|d| d.width) - .max_by_key(|x| NotNan::new(*x).unwrap()) - .unwrap(); - let total_height: f64 = dims_per_widget.iter().map(|d| d.height).sum(); - - let mut top_left = match orientation { - ContainerOrientation::TopLeft => ScreenPt::new(0.0, 0.0), - ContainerOrientation::TopRight => ScreenPt::new(ctx.canvas.window_width - total_width, 0.0), - ContainerOrientation::TopLeftButDownABit(y1) => ScreenPt::new(0.0, y1), - ContainerOrientation::Centered => { - let mut pt = ctx.canvas.center_to_screen_pt(); - pt.x -= total_width / 2.0; - pt.y -= total_height / 2.0; - pt - } - ContainerOrientation::Top(percent) => ScreenPt::new(ctx.canvas.window_width * percent, 0.0), - }; - for (w, dims) in widgets.into_iter().zip(dims_per_widget) { - w.set_pos(top_left); - top_left.y += dims.height; - } -} diff --git a/ezgui/src/lib.rs b/ezgui/src/lib.rs index 24a64c728f..c56ba06e01 100644 --- a/ezgui/src/lib.rs +++ b/ezgui/src/lib.rs @@ -10,8 +10,8 @@ mod color; mod drawing; mod event; mod event_ctx; +mod geom; mod input; -pub mod layout; mod managed; mod runner; mod screen_geom; @@ -22,18 +22,29 @@ mod widgets; pub use crate::backend::Drawable; pub use crate::canvas::{Canvas, HorizontalAlignment, VerticalAlignment}; pub use crate::color::Color; -pub use crate::drawing::{GeomBatch, GfxCtx, Prerender, RewriteColor}; +pub use crate::drawing::{GfxCtx, Prerender}; pub use crate::event::{hotkey, hotkeys, lctrl, Event, Key, MultiKey}; pub use crate::event_ctx::EventCtx; +pub use crate::geom::{GeomBatch, RewriteColor}; pub use crate::input::UserInput; pub use crate::managed::{Composite, ManagedWidget, Outcome}; pub use crate::runner::{run, EventLoopMode, Settings, GUI}; pub use crate::screen_geom::{ScreenDims, ScreenPt, ScreenRectangle}; pub use crate::text::{Line, Text, TextExt, TextSpan, HOTKEY_COLOR}; -pub use crate::widgets::{ - Autocomplete, Btn, Button, Choice, Filler, Histogram, ItemSlider, JustDraw, ModalMenu, Plot, - PlotOptions, Series, Slider, Warper, WarpingItemSlider, Wizard, WrappedWizard, -}; +pub use crate::widgets::autocomplete::Autocomplete; +pub use crate::widgets::button::{Btn, Button}; +pub use crate::widgets::checkbox::Checkbox; +pub(crate) use crate::widgets::dropdown::Dropdown; +pub use crate::widgets::filler::Filler; +pub use crate::widgets::histogram::Histogram; +pub use crate::widgets::modal_menu::ModalMenu; +pub use crate::widgets::no_op::JustDraw; +pub use crate::widgets::plot::{Plot, PlotOptions, Series}; +pub(crate) use crate::widgets::popup_menu::PopupMenu; +pub use crate::widgets::slider::{ItemSlider, Slider, WarpingItemSlider}; +pub(crate) use crate::widgets::text_box::TextBox; +pub use crate::widgets::warper::Warper; +pub use crate::widgets::wizard::{Choice, Wizard, WrappedWizard}; pub enum InputResult { Canceled, diff --git a/ezgui/src/managed.rs b/ezgui/src/managed.rs index 028a90347e..037a3a13df 100644 --- a/ezgui/src/managed.rs +++ b/ezgui/src/managed.rs @@ -1,9 +1,8 @@ -use crate::layout::Widget; -use crate::widgets::{Checkbox, Dropdown, PopupMenu, TextBox}; +use crate::widgets::Widget; use crate::{ - Btn, Button, Choice, Color, Drawable, EventCtx, Filler, GeomBatch, GfxCtx, Histogram, - HorizontalAlignment, JustDraw, MultiKey, Plot, RewriteColor, ScreenDims, ScreenPt, - ScreenRectangle, Slider, Text, VerticalAlignment, + Btn, Button, Checkbox, Choice, Color, Drawable, Dropdown, EventCtx, Filler, GeomBatch, GfxCtx, + Histogram, HorizontalAlignment, JustDraw, MultiKey, Plot, PopupMenu, RewriteColor, ScreenDims, + ScreenPt, ScreenRectangle, Slider, Text, TextBox, VerticalAlignment, }; use abstutil::Cloneable; use geom::{Distance, Duration, Polygon}; diff --git a/ezgui/src/runner.rs b/ezgui/src/runner.rs index e4c7681f72..6c211d21cf 100644 --- a/ezgui/src/runner.rs +++ b/ezgui/src/runner.rs @@ -1,5 +1,6 @@ use crate::assets::Assets; -use crate::{widgets, Canvas, Event, EventCtx, GfxCtx, Key, Prerender, UserInput}; +use crate::widgets::screenshot::{screenshot_current, screenshot_everything}; +use crate::{Canvas, Event, EventCtx, GfxCtx, Key, Prerender, UserInput}; use geom::Duration; use instant::Instant; use std::cell::Cell; @@ -297,10 +298,10 @@ pub fn run G>(settings: Settings, max_x, max_y, } => { - widgets::screenshot_everything(&mut state, &dir, &prerender, zoom, max_x, max_y); + screenshot_everything(&mut state, &dir, &prerender, zoom, max_x, max_y); } EventLoopMode::ScreenCaptureCurrentShot => { - widgets::screenshot_current(&mut state, &prerender); + screenshot_current(&mut state, &prerender); } } }); diff --git a/ezgui/src/widgets/button.rs b/ezgui/src/widgets/button.rs index 4790954f68..d97eed0c19 100644 --- a/ezgui/src/widgets/button.rs +++ b/ezgui/src/widgets/button.rs @@ -1,4 +1,4 @@ -use crate::layout::Widget; +use crate::widgets::Widget; use crate::{ text, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, ManagedWidget, MultiKey, RewriteColor, ScreenDims, ScreenPt, Text, diff --git a/ezgui/src/widgets/checkbox.rs b/ezgui/src/widgets/checkbox.rs index fa596988bd..a60110e431 100644 --- a/ezgui/src/widgets/checkbox.rs +++ b/ezgui/src/widgets/checkbox.rs @@ -1,4 +1,4 @@ -use crate::layout::Widget; +use crate::widgets::Widget; use crate::{Button, EventCtx, GfxCtx, ScreenDims, ScreenPt}; pub struct Checkbox { @@ -24,7 +24,7 @@ impl Checkbox { } } - // If true, layout should be recomputed. + // If true, widgets should be recomputed. pub(crate) fn event(&mut self, ctx: &mut EventCtx) -> bool { self.btn.event(ctx); if self.btn.clicked() { diff --git a/ezgui/src/widgets/dropdown.rs b/ezgui/src/widgets/dropdown.rs index de48597ed2..51b4fc00ea 100644 --- a/ezgui/src/widgets/dropdown.rs +++ b/ezgui/src/widgets/dropdown.rs @@ -1,7 +1,6 @@ -use crate::layout::Widget; -use crate::widgets::PopupMenu; +use crate::widgets::Widget; use crate::{ - Btn, Button, Choice, Color, EventCtx, GfxCtx, InputResult, ScreenDims, ScreenPt, + Btn, Button, Choice, Color, EventCtx, GfxCtx, InputResult, PopupMenu, ScreenDims, ScreenPt, ScreenRectangle, }; use geom::{Polygon, Pt2D}; @@ -51,7 +50,7 @@ impl Dropdown { } } - // If true, layout should be recomputed. + // If true, widgets should be recomputed. pub fn event(&mut self, ctx: &mut EventCtx, our_rect: &ScreenRectangle) -> bool { if let Some(ref mut m) = self.menu { m.event(ctx); @@ -64,7 +63,7 @@ impl Dropdown { self.menu = None; self.current_idx = idx; let top_left = self.btn.top_left; - // TODO Recalculate layout when this happens... outline around button should + // TODO Recalculate widgets when this happens... outline around button should // change self.btn = make_btn(ctx, &self.choices[self.current_idx].label, &self.label); self.btn.set_pos(top_left); diff --git a/ezgui/src/widgets/filler.rs b/ezgui/src/widgets/filler.rs index fd3df24f51..a51770951f 100644 --- a/ezgui/src/widgets/filler.rs +++ b/ezgui/src/widgets/filler.rs @@ -1,7 +1,7 @@ -use crate::layout::Widget; +use crate::widgets::Widget; use crate::{ScreenDims, ScreenPt}; -// Doesn't do anything by itself, just used for layouting. Something else reaches in, asks for the +// Doesn't do anything by itself, just used for widgetsing. Something else reaches in, asks for the // ScreenRectangle to use. pub struct Filler { pub(crate) top_left: ScreenPt, diff --git a/ezgui/src/widgets/histogram.rs b/ezgui/src/widgets/histogram.rs index 8da05d017d..f83dbd06bd 100644 --- a/ezgui/src/widgets/histogram.rs +++ b/ezgui/src/widgets/histogram.rs @@ -1,4 +1,4 @@ -use crate::layout::Widget; +use crate::widgets::Widget; use crate::{ Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, ManagedWidget, ScreenDims, ScreenPt, Text, TextExt, diff --git a/ezgui/src/widgets/mod.rs b/ezgui/src/widgets/mod.rs index b354bf4d9d..22bf5fd764 100644 --- a/ezgui/src/widgets/mod.rs +++ b/ezgui/src/widgets/mod.rs @@ -1,31 +1,66 @@ -mod autocomplete; -mod button; -mod checkbox; -mod dropdown; -mod filler; -mod histogram; -mod modal_menu; -mod no_op; -mod plot; -mod popup_menu; -mod screenshot; -mod slider; -mod text_box; -mod warper; -mod wizard; +pub mod autocomplete; +pub mod button; +pub mod checkbox; +pub mod dropdown; +pub mod filler; +pub mod histogram; +pub mod modal_menu; +pub mod no_op; +pub mod plot; +pub mod popup_menu; +pub mod screenshot; +pub mod slider; +pub mod text_box; +pub mod warper; +pub mod wizard; -pub use self::autocomplete::Autocomplete; -pub use self::button::{Btn, Button}; -pub use self::checkbox::Checkbox; -pub(crate) use self::dropdown::Dropdown; -pub use self::filler::Filler; -pub use self::histogram::Histogram; -pub use self::modal_menu::ModalMenu; -pub use self::no_op::JustDraw; -pub use self::plot::{Plot, PlotOptions, Series}; -pub(crate) use self::popup_menu::PopupMenu; -pub(crate) use self::screenshot::{screenshot_current, screenshot_everything}; -pub use self::slider::{ItemSlider, Slider, WarpingItemSlider}; -pub(crate) use self::text_box::TextBox; -pub use self::warper::Warper; -pub use self::wizard::{Choice, Wizard, WrappedWizard}; +use crate::{EventCtx, ScreenDims, ScreenPt}; +use ordered_float::NotNan; + +pub trait Widget { + fn get_dims(&self) -> ScreenDims; + fn set_pos(&mut self, top_left: ScreenPt); +} + +#[derive(Clone, Copy)] +pub enum ContainerOrientation { + TopLeft, + TopRight, + TopLeftButDownABit(f64), + Centered, + // Place the widget this percentage along the width of the screen + Top(f64), +} + +pub fn stack_vertically( + orientation: ContainerOrientation, + ctx: &EventCtx, + widgets: Vec<&mut dyn Widget>, +) { + assert!(!widgets.is_empty()); + + let dims_per_widget: Vec = widgets.iter().map(|w| w.get_dims()).collect(); + let total_width = dims_per_widget + .iter() + .map(|d| d.width) + .max_by_key(|x| NotNan::new(*x).unwrap()) + .unwrap(); + let total_height: f64 = dims_per_widget.iter().map(|d| d.height).sum(); + + let mut top_left = match orientation { + ContainerOrientation::TopLeft => ScreenPt::new(0.0, 0.0), + ContainerOrientation::TopRight => ScreenPt::new(ctx.canvas.window_width - total_width, 0.0), + ContainerOrientation::TopLeftButDownABit(y1) => ScreenPt::new(0.0, y1), + ContainerOrientation::Centered => { + let mut pt = ctx.canvas.center_to_screen_pt(); + pt.x -= total_width / 2.0; + pt.y -= total_height / 2.0; + pt + } + ContainerOrientation::Top(percent) => ScreenPt::new(ctx.canvas.window_width * percent, 0.0), + }; + for (w, dims) in widgets.into_iter().zip(dims_per_widget) { + w.set_pos(top_left); + top_left.y += dims.height; + } +} diff --git a/ezgui/src/widgets/modal_menu.rs b/ezgui/src/widgets/modal_menu.rs index 7d593fac0f..1b19869589 100644 --- a/ezgui/src/widgets/modal_menu.rs +++ b/ezgui/src/widgets/modal_menu.rs @@ -1,7 +1,5 @@ -use crate::layout::Widget; -use crate::{ - layout, text, EventCtx, GfxCtx, Line, MultiKey, ScreenDims, ScreenPt, ScreenRectangle, Text, -}; +use crate::widgets::{stack_vertically, ContainerOrientation, Widget}; +use crate::{text, EventCtx, GfxCtx, Line, MultiKey, ScreenDims, ScreenPt, ScreenRectangle, Text}; pub struct ModalMenu { title: String, @@ -10,7 +8,7 @@ pub struct ModalMenu { choices: Vec, // This can be inactive entries too. hovering_idx: Option, - standalone_layout: Option, + standalone_widgets: Option, top_left: ScreenPt, dims: ScreenDims, @@ -41,7 +39,7 @@ impl ModalMenu { }) .collect(), hovering_idx: None, - standalone_layout: Some(layout::ContainerOrientation::TopRight), + standalone_widgets: Some(ContainerOrientation::TopRight), top_left: ScreenPt::new(0.0, 0.0), dims: ScreenDims::new(0.0, 0.0), @@ -52,14 +50,14 @@ impl ModalMenu { } // It's part of something bigger - pub fn disable_standalone_layout(mut self) -> ModalMenu { - assert!(self.standalone_layout.is_some()); - self.standalone_layout = None; + pub fn disable_standalone_widgets(mut self) -> ModalMenu { + assert!(self.standalone_widgets.is_some()); + self.standalone_widgets = None; self } - pub fn set_standalone_layout(mut self, layout: layout::ContainerOrientation) -> ModalMenu { - self.standalone_layout = Some(layout); + pub fn set_standalone_widgets(mut self, widgets: ContainerOrientation) -> ModalMenu { + self.standalone_widgets = Some(widgets); self } @@ -73,8 +71,8 @@ impl ModalMenu { panic!("Caller didn't consume modal action '{}'", action); } - if let Some(o) = self.standalone_layout { - layout::stack_vertically(o, ctx, vec![self]); + if let Some(o) = self.standalone_widgets { + stack_vertically(o, ctx, vec![self]); self.dims = self.calculate_txt().dims(&ctx.prerender.assets); } diff --git a/ezgui/src/widgets/no_op.rs b/ezgui/src/widgets/no_op.rs index 27c1712e3b..f96bbafa8d 100644 --- a/ezgui/src/widgets/no_op.rs +++ b/ezgui/src/widgets/no_op.rs @@ -1,10 +1,10 @@ -use crate::layout::Widget; use crate::svg; +use crate::widgets::Widget; use crate::{ Drawable, EventCtx, GeomBatch, GfxCtx, ManagedWidget, RewriteColor, ScreenDims, ScreenPt, Text, }; -// Just draw something. A widget just so layouting works. +// Just draw something. A widget just so widgetsing works. pub struct JustDraw { pub(crate) draw: Drawable, diff --git a/ezgui/src/widgets/plot.rs b/ezgui/src/widgets/plot.rs index d027a831c1..d41a70b876 100644 --- a/ezgui/src/widgets/plot.rs +++ b/ezgui/src/widgets/plot.rs @@ -1,4 +1,4 @@ -use crate::layout::Widget; +use crate::widgets::Widget; use crate::{ Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, ManagedWidget, ScreenDims, ScreenPt, ScreenRectangle, Text, TextExt, diff --git a/ezgui/src/widgets/popup_menu.rs b/ezgui/src/widgets/popup_menu.rs index 9b852460df..34a454f00e 100644 --- a/ezgui/src/widgets/popup_menu.rs +++ b/ezgui/src/widgets/popup_menu.rs @@ -1,4 +1,4 @@ -use crate::layout::Widget; +use crate::widgets::Widget; use crate::{ hotkey, text, Choice, EventCtx, GfxCtx, InputResult, Key, Line, ScreenDims, ScreenPt, ScreenRectangle, Text, diff --git a/ezgui/src/widgets/slider.rs b/ezgui/src/widgets/slider.rs index aff98ceabb..31e86c7320 100644 --- a/ezgui/src/widgets/slider.rs +++ b/ezgui/src/widgets/slider.rs @@ -1,4 +1,4 @@ -use crate::layout::{stack_vertically, ContainerOrientation, Widget}; +use crate::widgets::{stack_vertically, ContainerOrientation, Widget}; use crate::{ hotkey, Color, Drawable, EventCtx, EventLoopMode, GeomBatch, GfxCtx, Key, Line, ModalMenu, MultiKey, ScreenDims, ScreenPt, ScreenRectangle, Text, Warper, @@ -257,7 +257,7 @@ impl ItemSlider { (hotkey(Key::Dot), last.as_str()), ]); - let menu = ModalMenu::new(menu_title, choices, ctx).disable_standalone_layout(); + let menu = ModalMenu::new(menu_title, choices, ctx).disable_standalone_widgets(); ItemSlider { items, // TODO Number of items diff --git a/ezgui/src/widgets/text_box.rs b/ezgui/src/widgets/text_box.rs index 28b002e307..6588b7f18b 100644 --- a/ezgui/src/widgets/text_box.rs +++ b/ezgui/src/widgets/text_box.rs @@ -1,4 +1,4 @@ -use crate::layout::Widget; +use crate::widgets::Widget; use crate::{ text, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, ScreenDims, ScreenPt, ScreenRectangle, Text, diff --git a/ezgui/src/widgets/wizard.rs b/ezgui/src/widgets/wizard.rs index 76382b9650..85da71af59 100644 --- a/ezgui/src/widgets/wizard.rs +++ b/ezgui/src/widgets/wizard.rs @@ -1,7 +1,6 @@ -use crate::widgets::PopupMenu; use crate::{ hotkey, Btn, Color, Composite, EventCtx, GfxCtx, HorizontalAlignment, InputResult, Key, Line, - ManagedWidget, MultiKey, Outcome, Text, VerticalAlignment, + ManagedWidget, MultiKey, Outcome, PopupMenu, Text, VerticalAlignment, }; use abstutil::Cloneable; use std::collections::VecDeque;