mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
consolidate ezgui internals a little, and start a little bit of rustdoc
This commit is contained in:
parent
9644ff24be
commit
81c5ae3700
@ -5,6 +5,7 @@ authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
||||
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"]
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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<Polygon>) {
|
||||
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<I: Into<String>>(
|
||||
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 {
|
||||
|
178
ezgui/src/geom.rs
Normal file
178
ezgui/src/geom.rs
Normal file
@ -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<Polygon>) {
|
||||
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<I: Into<String>>(
|
||||
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),
|
||||
}
|
@ -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<ScreenDims> = 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;
|
||||
}
|
||||
}
|
@ -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<T: Clone> {
|
||||
Canceled,
|
||||
|
@ -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};
|
||||
|
@ -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: 'static + GUI, F: FnOnce(&mut EventCtx) -> 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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<ScreenDims> = 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Choice>,
|
||||
// This can be inactive entries too.
|
||||
hovering_idx: Option<usize>,
|
||||
standalone_layout: Option<layout::ContainerOrientation>,
|
||||
standalone_widgets: Option<ContainerOrientation>,
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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<T> ItemSlider<T> {
|
||||
(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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user