mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
let plots have a chance to update themselves. hacky.
This commit is contained in:
parent
694b1f6cc9
commit
5868fe4736
@ -49,7 +49,7 @@ 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, Outcome, Widget};
|
||||
pub use crate::managed::{Composite, Widget};
|
||||
pub use crate::runner::{run, EventLoopMode, Settings, GUI};
|
||||
pub use crate::screen_geom::{ScreenDims, ScreenPt, ScreenRectangle};
|
||||
pub use crate::style::Style;
|
||||
@ -70,7 +70,7 @@ pub use crate::widgets::plot::{Plot, PlotOptions, Series};
|
||||
pub use crate::widgets::slider::Slider;
|
||||
pub use crate::widgets::spinner::Spinner;
|
||||
pub(crate) use crate::widgets::text_box::TextBox;
|
||||
pub use crate::widgets::WidgetImpl;
|
||||
pub use crate::widgets::{Outcome, WidgetImpl, WidgetOutput};
|
||||
|
||||
pub(crate) enum InputResult<T: Clone> {
|
||||
Canceled,
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::widgets::containers::{Container, Nothing};
|
||||
use crate::{
|
||||
Autocomplete, Button, Checkbox, Choice, Color, Drawable, Dropdown, EventCtx, Filler, GeomBatch,
|
||||
GfxCtx, HorizontalAlignment, JustDraw, Menu, PersistentSplit, RewriteColor, ScreenDims,
|
||||
ScreenPt, ScreenRectangle, Slider, Spinner, TextBox, VerticalAlignment, WidgetImpl,
|
||||
GfxCtx, HorizontalAlignment, JustDraw, Menu, Outcome, PersistentSplit, RewriteColor,
|
||||
ScreenDims, ScreenPt, ScreenRectangle, Slider, Spinner, TextBox, VerticalAlignment, WidgetImpl,
|
||||
WidgetOutput,
|
||||
};
|
||||
use geom::{Distance, Polygon};
|
||||
use std::collections::HashSet;
|
||||
@ -425,6 +426,9 @@ impl Widget {
|
||||
pub(crate) fn take_just_draw(self) -> JustDraw {
|
||||
*self.widget.downcast::<JustDraw>().ok().unwrap()
|
||||
}
|
||||
pub(crate) fn take_checkbox(self) -> Checkbox {
|
||||
*self.widget.downcast::<Checkbox>().ok().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
enum Dims {
|
||||
@ -453,10 +457,6 @@ pub struct Composite {
|
||||
clip_rect: Option<ScreenRectangle>,
|
||||
}
|
||||
|
||||
pub enum Outcome {
|
||||
Clicked(String),
|
||||
}
|
||||
|
||||
const SCROLL_SPEED: f64 = 5.0;
|
||||
|
||||
impl Composite {
|
||||
@ -586,12 +586,27 @@ impl Composite {
|
||||
}
|
||||
|
||||
let before = self.scroll_offset();
|
||||
let mut redo_layout = false;
|
||||
let result = self.top_level.widget.event(ctx, &mut redo_layout);
|
||||
if self.scroll_offset() != before || redo_layout {
|
||||
let mut output = WidgetOutput {
|
||||
redo_layout: false,
|
||||
outcome: None,
|
||||
plot_changed: Vec::new(),
|
||||
};
|
||||
self.top_level.widget.event(ctx, &mut output);
|
||||
if self.scroll_offset() != before || output.redo_layout {
|
||||
self.recompute_layout(ctx, true);
|
||||
}
|
||||
result
|
||||
|
||||
// TODO Fantastic hack
|
||||
for ((plot_id, checkbox_label), enabled) in output.plot_changed {
|
||||
// TODO Can't downcast and ignore the type param
|
||||
self.top_level
|
||||
.find_mut(&plot_id)
|
||||
.unwrap()
|
||||
.widget
|
||||
.update_series(checkbox_label, enabled);
|
||||
}
|
||||
|
||||
output.outcome
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Choice, EventCtx, GfxCtx, InputResult, Menu, Outcome, ScreenDims, ScreenPt, TextBox, Widget,
|
||||
WidgetImpl,
|
||||
Choice, EventCtx, GfxCtx, InputResult, Menu, ScreenDims, ScreenPt, TextBox, Widget, WidgetImpl,
|
||||
WidgetOutput,
|
||||
};
|
||||
use simsearch::SimSearch;
|
||||
use std::collections::HashMap;
|
||||
@ -88,16 +88,16 @@ impl<T: 'static + Clone> WidgetImpl for Autocomplete<T> {
|
||||
));
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput) {
|
||||
assert!(self.chosen_values.is_none());
|
||||
|
||||
self.tb.event(ctx, redo_layout);
|
||||
self.tb.event(ctx, output);
|
||||
if self.tb.get_line() != self.current_line {
|
||||
self.current_line = self.tb.get_line();
|
||||
self.recalc_menu(ctx);
|
||||
*redo_layout = true;
|
||||
output.redo_layout = true;
|
||||
} else {
|
||||
self.menu.event(ctx, redo_layout);
|
||||
self.menu.event(ctx, output);
|
||||
match self.menu.state {
|
||||
InputResult::StillActive => {}
|
||||
// Ignore this and make sure the Composite has a quit control
|
||||
@ -109,8 +109,6 @@ impl<T: 'static + Clone> WidgetImpl for Autocomplete<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
svg, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, MultiKey, Outcome,
|
||||
RewriteColor, ScreenDims, ScreenPt, Text, Widget, WidgetImpl,
|
||||
RewriteColor, ScreenDims, ScreenPt, Text, Widget, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use geom::Polygon;
|
||||
|
||||
@ -68,7 +68,7 @@ impl WidgetImpl for Button {
|
||||
self.top_left = top_left;
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput) {
|
||||
if ctx.redo_mouseover() {
|
||||
if let Some(pt) = ctx.canvas.get_cursor_in_screen_space() {
|
||||
self.hovering = self
|
||||
@ -81,21 +81,21 @@ impl WidgetImpl for Button {
|
||||
}
|
||||
if self.hovering && ctx.normal_left_click() {
|
||||
self.hovering = false;
|
||||
return Some(Outcome::Clicked(self.action.clone()));
|
||||
output.outcome = Some(Outcome::Clicked(self.action.clone()));
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(ref hotkey) = self.hotkey {
|
||||
if ctx.input.new_was_pressed(hotkey) {
|
||||
self.hovering = false;
|
||||
return Some(Outcome::Clicked(self.action.clone()));
|
||||
output.outcome = Some(Outcome::Clicked(self.action.clone()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if self.hovering {
|
||||
ctx.cursor_clickable();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,11 +1,14 @@
|
||||
use crate::{
|
||||
Btn, Button, EventCtx, GfxCtx, MultiKey, Outcome, ScreenDims, ScreenPt, Widget, WidgetImpl,
|
||||
Btn, Button, EventCtx, GfxCtx, MultiKey, ScreenDims, ScreenPt, Widget, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
|
||||
pub struct Checkbox {
|
||||
pub(crate) enabled: bool,
|
||||
btn: Button,
|
||||
other_btn: Button,
|
||||
|
||||
// TODO Biiiit of a hack. If Plot could embed a Composite, that'd actually work better.
|
||||
cb_to_plot: Option<(String, String)>,
|
||||
}
|
||||
|
||||
impl Checkbox {
|
||||
@ -16,12 +19,14 @@ impl Checkbox {
|
||||
enabled,
|
||||
btn: true_btn.take_btn(),
|
||||
other_btn: false_btn.take_btn(),
|
||||
cb_to_plot: None,
|
||||
}))
|
||||
} else {
|
||||
Widget::new(Box::new(Checkbox {
|
||||
enabled,
|
||||
btn: false_btn.take_btn(),
|
||||
other_btn: true_btn.take_btn(),
|
||||
cb_to_plot: None,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -35,6 +40,11 @@ impl Checkbox {
|
||||
.outline(ctx.style().outline_thickness, ctx.style().outline_color)
|
||||
.named(label)
|
||||
}
|
||||
|
||||
pub(crate) fn callback_to_plot(mut self, plot_id: &str, checkbox_label: &str) -> Checkbox {
|
||||
self.cb_to_plot = Some((plot_id.to_string(), checkbox_label.to_string()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for Checkbox {
|
||||
@ -46,15 +56,17 @@ impl WidgetImpl for Checkbox {
|
||||
self.btn.set_pos(top_left);
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
|
||||
if self.btn.event(ctx, redo_layout).is_some() {
|
||||
fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput) {
|
||||
self.btn.event(ctx, output);
|
||||
if output.outcome.take().is_some() {
|
||||
std::mem::swap(&mut self.btn, &mut self.other_btn);
|
||||
self.btn.set_pos(self.other_btn.top_left);
|
||||
self.enabled = !self.enabled;
|
||||
*redo_layout = true;
|
||||
output.redo_layout = true;
|
||||
if let Some(ref pair) = self.cb_to_plot {
|
||||
output.plot_changed.push((pair.clone(), self.enabled));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{EventCtx, GfxCtx, Outcome, ScreenDims, ScreenPt, Widget, WidgetImpl};
|
||||
use crate::{EventCtx, GfxCtx, ScreenDims, ScreenPt, Widget, WidgetImpl, WidgetOutput};
|
||||
|
||||
pub struct Nothing {}
|
||||
|
||||
@ -11,7 +11,7 @@ impl WidgetImpl for Nothing {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _output: &mut WidgetOutput) {
|
||||
unreachable!()
|
||||
}
|
||||
fn draw(&self, _g: &mut GfxCtx) {
|
||||
@ -40,13 +40,13 @@ impl WidgetImpl for Container {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput) {
|
||||
for w in &mut self.members {
|
||||
if let Some(o) = w.widget.event(ctx, redo_layout) {
|
||||
return Some(o);
|
||||
w.widget.event(ctx, output);
|
||||
if output.outcome.is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Btn, Button, Choice, Color, EventCtx, GeomBatch, GfxCtx, InputResult, Menu, Outcome,
|
||||
ScreenDims, ScreenPt, ScreenRectangle, WidgetImpl,
|
||||
Btn, Button, Choice, Color, EventCtx, GeomBatch, GfxCtx, InputResult, Menu, ScreenDims,
|
||||
ScreenPt, ScreenRectangle, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use geom::{Distance, Polygon, Pt2D};
|
||||
|
||||
@ -56,9 +56,9 @@ impl<T: 'static + Clone> WidgetImpl for Dropdown<T> {
|
||||
self.btn.set_pos(top_left);
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput) {
|
||||
if let Some(ref mut m) = self.menu {
|
||||
m.event(ctx, redo_layout);
|
||||
m.event(ctx, output);
|
||||
match m.state {
|
||||
InputResult::StillActive => {}
|
||||
InputResult::Canceled => {
|
||||
@ -75,11 +75,12 @@ impl<T: 'static + Clone> WidgetImpl for Dropdown<T> {
|
||||
self.blank_btn_label,
|
||||
);
|
||||
self.btn.set_pos(top_left);
|
||||
*redo_layout = true;
|
||||
output.redo_layout = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.btn.event(ctx, redo_layout).is_some() {
|
||||
self.btn.event(ctx, output);
|
||||
if output.outcome.take().is_some() {
|
||||
// TODO set current idx in menu
|
||||
let mut menu = Menu::new(
|
||||
ctx,
|
||||
@ -104,8 +105,6 @@ impl<T: 'static + Clone> WidgetImpl for Dropdown<T> {
|
||||
self.menu = Some(menu);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{EventCtx, GfxCtx, Outcome, ScreenDims, ScreenPt, Widget, WidgetImpl};
|
||||
use crate::{EventCtx, GfxCtx, ScreenDims, ScreenPt, Widget, WidgetImpl, WidgetOutput};
|
||||
|
||||
// Doesn't do anything by itself, just used for widgetsing. Something else reaches in, asks for the
|
||||
// ScreenRectangle to use.
|
||||
@ -25,8 +25,6 @@ impl WidgetImpl for Filler {
|
||||
self.top_left = top_left;
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
None
|
||||
}
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _output: &mut WidgetOutput) {}
|
||||
fn draw(&self, _g: &mut GfxCtx) {}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, Outcome, ScreenDims, ScreenPt, Text,
|
||||
TextExt, Widget, WidgetImpl,
|
||||
Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, ScreenDims, ScreenPt, Text, TextExt,
|
||||
Widget, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use abstutil::prettyprint_usize;
|
||||
use geom::{Distance, Duration, Polygon, Pt2D};
|
||||
@ -113,9 +113,7 @@ impl WidgetImpl for Histogram {
|
||||
self.top_left = top_left;
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
None
|
||||
}
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _output: &mut WidgetOutput) {}
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
g.redraw_at(self.top_left, &self.draw);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
svg, Drawable, EventCtx, GeomBatch, GfxCtx, Outcome, RewriteColor, ScreenDims, ScreenPt,
|
||||
Widget, WidgetImpl,
|
||||
svg, Drawable, EventCtx, GeomBatch, GfxCtx, RewriteColor, ScreenDims, ScreenPt, Widget,
|
||||
WidgetImpl, WidgetOutput,
|
||||
};
|
||||
|
||||
// Just draw something. A widget just so widgetsing works.
|
||||
@ -50,9 +50,7 @@ impl WidgetImpl for JustDraw {
|
||||
self.top_left = top_left;
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
None
|
||||
}
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _output: &mut WidgetOutput) {}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
g.redraw_at(self.top_left, &self.draw);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
hotkey, text, Choice, EventCtx, GfxCtx, InputResult, Key, Line, Outcome, ScreenDims, ScreenPt,
|
||||
ScreenRectangle, Text, Widget, WidgetImpl,
|
||||
hotkey, text, Choice, EventCtx, GfxCtx, InputResult, Key, Line, ScreenDims, ScreenPt,
|
||||
ScreenRectangle, Text, Widget, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use geom::Pt2D;
|
||||
|
||||
@ -79,9 +79,9 @@ impl<T: 'static + Clone> WidgetImpl for Menu<T> {
|
||||
self.top_left = top_left;
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _output: &mut WidgetOutput) {
|
||||
if self.choices.is_empty() {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
|
||||
match self.state {
|
||||
@ -123,14 +123,14 @@ impl<T: 'static + Clone> WidgetImpl for Menu<T> {
|
||||
if let Some(pt) = ctx.canvas.get_cursor_in_screen_space() {
|
||||
if rect.contains(pt) && choice.active {
|
||||
self.state = InputResult::Done(choice.label.clone(), choice.data.clone());
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
// Unconsume the click, it was in screen space, but not on us.
|
||||
ctx.input.unconsume_event();
|
||||
} else {
|
||||
// Clicked on the map? Cancel out
|
||||
self.state = InputResult::Canceled;
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -143,7 +143,7 @@ impl<T: 'static + Clone> WidgetImpl for Menu<T> {
|
||||
if let Some(ref hotkey) = choice.hotkey {
|
||||
if ctx.input.new_was_pressed(hotkey) {
|
||||
self.state = InputResult::Done(choice.label.clone(), choice.data.clone());
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,9 +153,9 @@ impl<T: 'static + Clone> WidgetImpl for Menu<T> {
|
||||
let choice = &self.choices[self.current_idx];
|
||||
if choice.active {
|
||||
self.state = InputResult::Done(choice.label.clone(), choice.data.clone());
|
||||
return None;
|
||||
return;
|
||||
} else {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
} else if ctx.input.new_was_pressed(&hotkey(Key::UpArrow).unwrap()) {
|
||||
if self.current_idx > 0 {
|
||||
@ -166,8 +166,6 @@ impl<T: 'static + Clone> WidgetImpl for Menu<T> {
|
||||
self.current_idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -13,7 +13,7 @@ pub mod slider;
|
||||
pub mod spinner;
|
||||
pub mod text_box;
|
||||
|
||||
use crate::{EventCtx, GfxCtx, Outcome, ScreenDims, ScreenPt};
|
||||
use crate::{EventCtx, GfxCtx, ScreenDims, ScreenPt};
|
||||
|
||||
/// Create a new widget by implementing this trait. You can instantiate your widget by calling
|
||||
/// `Widget::new(Box::new(instance of your new widget))`, which gives you the usual style options.
|
||||
@ -23,12 +23,31 @@ pub trait WidgetImpl: downcast_rs::Downcast {
|
||||
fn get_dims(&self) -> ScreenDims;
|
||||
/// Your widget's top left corner should be here. Handle mouse events and draw appropriately.
|
||||
fn set_pos(&mut self, top_left: ScreenPt);
|
||||
/// Your chance to react to an event. If this event should trigger layouting to be recalculated
|
||||
/// (because this widget changes dimensions), set `redo_layout` to true. Most widgets should
|
||||
/// return `None` instead of an `Outcome`.
|
||||
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome>;
|
||||
/// Your chance to react to an event. Any side effects outside of this widget are communicated
|
||||
/// through the output.
|
||||
fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput);
|
||||
/// Draw the widget. Be sure to draw relative to the top-left specified by `set_pos`.
|
||||
fn draw(&self, g: &mut GfxCtx);
|
||||
|
||||
/// Internal hack. Don't override.
|
||||
fn update_series(&mut self, _label: String, _enabled: bool) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Outcome {
|
||||
Clicked(String),
|
||||
}
|
||||
|
||||
pub struct WidgetOutput {
|
||||
/// This widget changed dimensions, so recalculate layout.
|
||||
pub redo_layout: bool,
|
||||
/// This widget produced an Outcome, and event handling should immediately stop. Most widgets
|
||||
/// shouldn't set this.
|
||||
pub outcome: Option<Outcome>,
|
||||
|
||||
/// Internal hack.
|
||||
pub(crate) plot_changed: Vec<((String, String), bool)>,
|
||||
}
|
||||
|
||||
downcast_rs::impl_downcast!(WidgetImpl);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Btn, Button, Choice, Color, Dropdown, EventCtx, GeomBatch, GfxCtx, JustDraw, MultiKey, Outcome,
|
||||
ScreenDims, ScreenPt, Widget, WidgetImpl,
|
||||
Btn, Button, Choice, Color, Dropdown, EventCtx, GeomBatch, GfxCtx, JustDraw, MultiKey,
|
||||
ScreenDims, ScreenPt, Widget, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use geom::Polygon;
|
||||
|
||||
@ -67,12 +67,13 @@ impl<T: 'static + Clone + PartialEq> WidgetImpl for PersistentSplit<T> {
|
||||
));
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
|
||||
if let Some(o) = self.btn.event(ctx, redo_layout) {
|
||||
return Some(o);
|
||||
fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput) {
|
||||
self.btn.event(ctx, output);
|
||||
if output.outcome.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.dropdown.event(ctx, redo_layout);
|
||||
self.dropdown.event(ctx, output);
|
||||
let new_value = self.dropdown.current_value();
|
||||
if new_value != self.current_value {
|
||||
self.current_value = new_value;
|
||||
@ -81,10 +82,8 @@ impl<T: 'static + Clone + PartialEq> WidgetImpl for PersistentSplit<T> {
|
||||
self.btn = Btn::plaintext(self.dropdown.current_value_label())
|
||||
.build(ctx, label, hotkey)
|
||||
.take_btn();
|
||||
*redo_layout = true;
|
||||
output.redo_layout = true;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, Outcome, ScreenDims, ScreenPt,
|
||||
ScreenRectangle, Text, TextExt, Widget, WidgetImpl,
|
||||
Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, Line, ScreenDims, ScreenPt,
|
||||
ScreenRectangle, Text, TextExt, Widget, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use abstutil::prettyprint_usize;
|
||||
use geom::{Angle, Bounds, Circle, Distance, Duration, FindClosest, PolyLine, Pt2D, Time};
|
||||
@ -36,7 +36,8 @@ impl PlotOptions {
|
||||
}
|
||||
|
||||
impl<T: Yvalue<T>> Plot<T> {
|
||||
pub fn new(ctx: &EventCtx, series: Vec<Series<T>>, opts: PlotOptions) -> Widget {
|
||||
// ID must be unique in a Composite
|
||||
pub fn new(ctx: &EventCtx, id: &str, series: Vec<Series<T>>, opts: PlotOptions) -> Widget {
|
||||
let legend = if series.len() == 1 {
|
||||
let radius = 15.0;
|
||||
// Can't hide if there's just one series
|
||||
@ -58,7 +59,11 @@ impl<T: Yvalue<T>> Plot<T> {
|
||||
.iter()
|
||||
.map(|s| {
|
||||
// TODO Colored checkbox
|
||||
Checkbox::text(ctx, &s.label, None, true)
|
||||
Widget::new(Box::new(
|
||||
Checkbox::text(ctx, &s.label, None, true)
|
||||
.take_checkbox()
|
||||
.callback_to_plot(id, &s.label),
|
||||
))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
@ -220,7 +225,10 @@ impl<T: Yvalue<T>> Plot<T> {
|
||||
// Don't let the x-axis fill the parent container
|
||||
Widget::row(vec![Widget::col(vec![
|
||||
legend,
|
||||
Widget::row(vec![y_axis.evenly_spaced(), Widget::new(Box::new(plot))]),
|
||||
Widget::row(vec![
|
||||
y_axis.evenly_spaced(),
|
||||
Widget::new(Box::new(plot)).named(id),
|
||||
]),
|
||||
x_axis.evenly_spaced(),
|
||||
])])
|
||||
}
|
||||
@ -235,9 +243,7 @@ impl<T: Yvalue<T>> WidgetImpl for Plot<T> {
|
||||
self.top_left = top_left;
|
||||
}
|
||||
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
None
|
||||
}
|
||||
fn event(&mut self, _ctx: &mut EventCtx, _output: &mut WidgetOutput) {}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
g.redraw_at(self.top_left, &self.draw_grid);
|
||||
@ -278,6 +284,16 @@ impl<T: Yvalue<T>> WidgetImpl for Plot<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_series(&mut self, label: String, enabled: bool) {
|
||||
for series in &mut self.series {
|
||||
if series.label == label {
|
||||
series.enabled = enabled;
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic!("Plot doesn't have a series {}", label);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Yvalue<T>: 'static + Copy + std::cmp::Ord {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
Color, Drawable, EventCtx, GeomBatch, GfxCtx, Outcome, ScreenDims, ScreenPt, ScreenRectangle,
|
||||
Widget, WidgetImpl,
|
||||
Color, Drawable, EventCtx, GeomBatch, GfxCtx, ScreenDims, ScreenPt, ScreenRectangle, Widget,
|
||||
WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use geom::Polygon;
|
||||
|
||||
@ -204,12 +204,10 @@ impl WidgetImpl for Slider {
|
||||
self.top_left = top_left;
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _output: &mut WidgetOutput) {
|
||||
if self.inner_event(ctx) {
|
||||
self.recalc(ctx);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
text, Btn, Button, EventCtx, GeomBatch, GfxCtx, Line, Outcome, ScreenDims, ScreenPt, Text,
|
||||
Widget, WidgetImpl,
|
||||
text, Btn, Button, EventCtx, GeomBatch, GfxCtx, Line, ScreenDims, ScreenPt, Text, Widget,
|
||||
WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use geom::{Polygon, Pt2D};
|
||||
|
||||
@ -67,18 +67,21 @@ impl WidgetImpl for Spinner {
|
||||
));
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, redo_layout: &mut bool) -> Option<Outcome> {
|
||||
if self.up.event(ctx, redo_layout).is_some() {
|
||||
fn event(&mut self, ctx: &mut EventCtx, output: &mut WidgetOutput) {
|
||||
self.up.event(ctx, output);
|
||||
if output.outcome.take().is_some() {
|
||||
if self.current != self.high {
|
||||
self.current += 1;
|
||||
}
|
||||
} else if self.down.event(ctx, redo_layout).is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.down.event(ctx, output);
|
||||
if output.outcome.take().is_some() {
|
||||
if self.current != self.low {
|
||||
self.current -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
text, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, Outcome, ScreenDims, ScreenPt,
|
||||
ScreenRectangle, Text, WidgetImpl,
|
||||
text, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, ScreenDims, ScreenPt, ScreenRectangle,
|
||||
Text, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
use geom::Polygon;
|
||||
|
||||
@ -63,7 +63,7 @@ impl WidgetImpl for TextBox {
|
||||
self.top_left = top_left;
|
||||
}
|
||||
|
||||
fn event(&mut self, ctx: &mut EventCtx, _redo_layout: &mut bool) -> Option<Outcome> {
|
||||
fn event(&mut self, ctx: &mut EventCtx, _output: &mut WidgetOutput) {
|
||||
if ctx.redo_mouseover() {
|
||||
if let Some(pt) = ctx.canvas.get_cursor_in_screen_space() {
|
||||
self.hovering = ScreenRectangle::top_left(self.top_left, self.dims).contains(pt);
|
||||
@ -80,7 +80,7 @@ impl WidgetImpl for TextBox {
|
||||
}
|
||||
|
||||
if !self.has_focus && !self.autofocus {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
if let Some(key) = ctx.input.any_key_pressed() {
|
||||
match key {
|
||||
@ -108,8 +108,6 @@ impl WidgetImpl for TextBox {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
|
@ -137,7 +137,7 @@ fn delays_over_time(ctx: &mut EventCtx, app: &App, id: BusRouteID) -> Widget {
|
||||
}
|
||||
Widget::col(vec![
|
||||
Line("Delays between stops").small_heading().draw(ctx),
|
||||
Plot::new(ctx, series, PlotOptions::new()).margin(10),
|
||||
Plot::new(ctx, "delay btwn stops", series, PlotOptions::new()).margin(10),
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ fn delay_plot(ctx: &EventCtx, app: &App, i: IntersectionID, opts: &DataOptions)
|
||||
}
|
||||
}
|
||||
|
||||
Plot::new(ctx, all_series, PlotOptions::new())
|
||||
Plot::new(ctx, "delay", all_series, PlotOptions::new())
|
||||
}
|
||||
|
||||
fn header(
|
||||
|
@ -472,7 +472,7 @@ fn throughput<F: Fn(&Analytics, Time) -> BTreeMap<TripMode, Vec<(Time, usize)>>>
|
||||
}
|
||||
}
|
||||
|
||||
Plot::new(ctx, series, PlotOptions::new())
|
||||
Plot::new(ctx, "throughput", series, PlotOptions::new())
|
||||
}
|
||||
|
||||
fn color_for_mode(m: TripMode, app: &App) -> Color {
|
||||
|
@ -353,6 +353,7 @@ fn make_elevation(ctx: &EventCtx, color: Color, walking: bool, path: &Path, map:
|
||||
// TODO Show roughly where we are in the trip; use distance covered by current path for this
|
||||
Plot::new(
|
||||
ctx,
|
||||
"elevation",
|
||||
vec![Series {
|
||||
label: if walking {
|
||||
"Elevation for walking"
|
||||
|
@ -161,6 +161,7 @@ fn trips_summary_prebaked(ctx: &EventCtx, app: &App) -> Widget {
|
||||
Line("Active agents").small_heading().draw(ctx),
|
||||
Plot::new(
|
||||
ctx,
|
||||
"active agents",
|
||||
vec![
|
||||
Series {
|
||||
label: "Baseline".to_string(),
|
||||
@ -229,6 +230,7 @@ fn trips_summary_not_prebaked(ctx: &EventCtx, app: &App) -> Widget {
|
||||
Line("Active agents").small_heading().draw(ctx),
|
||||
Plot::new(
|
||||
ctx,
|
||||
"active agents",
|
||||
vec![Series {
|
||||
label: "Active agents".to_string(),
|
||||
color: Color::RED,
|
||||
@ -291,6 +293,7 @@ fn finished_trips_plot(ctx: &EventCtx, app: &App) -> Widget {
|
||||
|
||||
let plot = Plot::new(
|
||||
ctx,
|
||||
"finished trips",
|
||||
lines
|
||||
.into_iter()
|
||||
.map(|(label, color, m)| Series {
|
||||
|
Loading…
Reference in New Issue
Block a user