Create a dummy widget to stash generic state in a Panel, and use it to plumb around the rectangular regions to the trip table filters. #574

The filter still doesn't make use of the regions yet.
This commit is contained in:
Dustin Carlino 2021-03-31 10:41:30 -07:00
parent dff923b3e6
commit c89e918fec
6 changed files with 91 additions and 9 deletions

View File

@ -1,3 +1,6 @@
use std::cell::RefCell;
use std::rc::Rc;
use geom::{Polygon, Pt2D};
use widgetry::{
Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, TextExt,
@ -9,12 +12,12 @@ use crate::app::{App, Transition};
// TODO Lift to widgetry
pub struct RectangularSelector {
panel: Panel,
region: Option<Polygon>,
region: Rc<RefCell<Option<Polygon>>>,
corners: Option<(Pt2D, Pt2D, bool)>,
}
impl RectangularSelector {
pub fn new(ctx: &mut EventCtx, region: Option<Polygon>) -> Box<dyn State<App>> {
pub fn new(ctx: &mut EventCtx, region: Rc<RefCell<Option<Polygon>>>) -> Box<dyn State<App>> {
Box::new(RectangularSelector {
panel: Panel::new(Widget::col(vec![
Widget::row(vec![
@ -59,6 +62,14 @@ impl State<App> for RectangularSelector {
match self.panel.event(ctx) {
Outcome::Clicked(x) => match x.as_ref() {
"close" => {
// TODO Apply button?
// TODO Some way to clear it
if let Some(rect) = self
.corners
.and_then(|(pt1, pt2, _)| Polygon::rectangle_two_corners(pt1, pt2))
{
self.region.replace(Some(rect));
}
return Transition::Pop;
}
_ => unreachable!(),
@ -71,7 +82,7 @@ impl State<App> for RectangularSelector {
fn draw(&self, g: &mut GfxCtx, _: &App) {
self.panel.draw(g);
if let Some(p) = self.region.clone() {
if let Some(p) = self.region.borrow().clone() {
g.draw_polygon(Color::BLUE.alpha(0.5), p);
}
if let Some((pt1, pt2, _)) = self.corners {

View File

@ -1,11 +1,12 @@
use std::collections::{BTreeSet, HashMap};
use abstutil::prettyprint_usize;
use geom::{Duration, Time};
use geom::{Duration, Polygon, Time};
use sim::{TripEndpoint, TripID, TripMode};
use widgetry::table::{Col, Filter, Table};
use widgetry::{
EventCtx, Filler, GfxCtx, Line, Outcome, Panel, State, TabController, Text, Toggle, Widget,
EventCtx, Filler, GfxCtx, Line, Outcome, Panel, Stash, State, TabController, Text, Toggle,
Widget,
};
use super::generic_trip_table::{open_trip_transition, preview_trip};
@ -138,9 +139,15 @@ impl State<App> for TripTable {
} else if self.table_tabs.handle_action(ctx, &x, &mut self.panel) {
// if true, tabs handled the action
} else if x == "filter starts" {
return Transition::Push(RectangularSelector::new(ctx, None));
return Transition::Push(RectangularSelector::new(
ctx,
self.panel.stash("starts_in"),
));
} else if x == "filter ends" {
return Transition::Push(RectangularSelector::new(ctx, None));
return Transition::Push(RectangularSelector::new(
ctx,
self.panel.stash("ends_in"),
));
} else {
unreachable!("unhandled action: {}", x)
}
@ -217,6 +224,8 @@ struct Filters {
modes: BTreeSet<TripMode>,
off_map_starts: bool,
off_map_ends: bool,
starts_in: Option<Polygon>,
ends_in: Option<Polygon>,
unmodified_trips: bool,
modified_trips: bool,
uncapped_trips: bool,
@ -307,6 +316,8 @@ fn make_table_finished_trips(app: &App) -> Table<App, FinishedTrip, Filters> {
modes: TripMode::all().into_iter().collect(),
off_map_starts: true,
off_map_ends: true,
starts_in: None,
ends_in: None,
unmodified_trips: true,
modified_trips: true,
uncapped_trips: true,
@ -320,6 +331,8 @@ fn make_table_finished_trips(app: &App) -> Table<App, FinishedTrip, Filters> {
Toggle::switch(ctx, "ending off-map", None, state.off_map_ends),
ctx.style().btn_plain.text("filter starts").build_def(ctx),
ctx.style().btn_plain.text("filter ends").build_def(ctx),
Stash::new("starts_in", state.starts_in.clone()),
Stash::new("ends_in", state.ends_in.clone()),
if app.primary.has_modified_trips {
Toggle::switch(
ctx,
@ -374,6 +387,8 @@ fn make_table_finished_trips(app: &App) -> Table<App, FinishedTrip, Filters> {
modes,
off_map_starts: panel.is_checked("starting off-map"),
off_map_ends: panel.is_checked("ending off-map"),
starts_in: panel.clone_stashed("starts_in"),
ends_in: panel.clone_stashed("ends_in"),
unmodified_trips: panel
.maybe_is_checked("trips unmodified by experiment")
.unwrap_or(true),
@ -525,6 +540,8 @@ fn make_table_cancelled_trips(app: &App) -> Table<App, CancelledTrip, Filters> {
modes: TripMode::all().into_iter().collect(),
off_map_starts: true,
off_map_ends: true,
starts_in: None,
ends_in: None,
unmodified_trips: true,
modified_trips: true,
uncapped_trips: true,
@ -550,6 +567,8 @@ fn make_table_cancelled_trips(app: &App) -> Table<App, CancelledTrip, Filters> {
modes,
off_map_starts: panel.is_checked("starting off-map"),
off_map_ends: panel.is_checked("ending off-map"),
starts_in: panel.clone_stashed("starts_in"),
ends_in: panel.clone_stashed("ends_in"),
unmodified_trips: true,
modified_trips: true,
uncapped_trips: true,
@ -640,6 +659,8 @@ fn make_table_unfinished_trips(app: &App) -> Table<App, UnfinishedTrip, Filters>
modes: TripMode::all().into_iter().collect(),
off_map_starts: true,
off_map_ends: true,
starts_in: None,
ends_in: None,
unmodified_trips: true,
modified_trips: true,
uncapped_trips: true,
@ -657,6 +678,8 @@ fn make_table_unfinished_trips(app: &App) -> Table<App, UnfinishedTrip, Filters>
modes,
off_map_starts: true,
off_map_ends: true,
starts_in: panel.clone_stashed("starts_in"),
ends_in: panel.clone_stashed("ends_in"),
unmodified_trips: true,
modified_trips: true,
uncapped_trips: true,

View File

@ -63,6 +63,7 @@ pub use crate::widgets::plots::{PlotOptions, Series};
pub use crate::widgets::scatter_plot::ScatterPlot;
pub use crate::widgets::slider::Slider;
pub use crate::widgets::spinner::Spinner;
pub use crate::widgets::stash::Stash;
pub use crate::widgets::table;
pub use crate::widgets::tabs::TabController;
pub(crate) use crate::widgets::text_box::TextBox;

View File

@ -33,6 +33,7 @@ pub mod plots;
pub mod scatter_plot;
pub mod slider;
pub mod spinner;
pub mod stash;
pub mod table;
pub mod tabs;
pub mod text_box;

View File

@ -1,4 +1,6 @@
use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
use stretch::geometry::Size;
use stretch::node::Stretch;
@ -11,8 +13,8 @@ use crate::widgets::slider;
use crate::widgets::Container;
use crate::{
Autocomplete, Button, Color, Dropdown, EventCtx, GfxCtx, HorizontalAlignment, Menu, Outcome,
PersistentSplit, ScreenDims, ScreenPt, ScreenRectangle, Slider, Spinner, TextBox, Toggle,
VerticalAlignment, Widget, WidgetImpl, WidgetOutput,
PersistentSplit, ScreenDims, ScreenPt, ScreenRectangle, Slider, Spinner, Stash, TextBox,
Toggle, VerticalAlignment, Widget, WidgetImpl, WidgetOutput,
};
pub struct Panel {
@ -406,6 +408,16 @@ impl Panel {
self.find::<Autocomplete<T>>(name).final_value()
}
/// Grab a stashed value, with the ability to pass it around and modify it.
pub fn stash<T: 'static>(&self, name: &str) -> Rc<RefCell<T>> {
self.find::<Stash<T>>(name).get_value()
}
/// Grab a stashed value and clone it.
pub fn clone_stashed<T: 'static + Clone>(&self, name: &str) -> T {
self.find::<Stash<T>>(name).get_value().borrow().clone()
}
pub fn is_button_enabled(&self, name: &str) -> bool {
self.find::<Button>(name).is_enabled()
}

View File

@ -0,0 +1,34 @@
use std::cell::RefCell;
use std::rc::Rc;
use crate::{EventCtx, GfxCtx, ScreenDims, ScreenPt, Widget, WidgetImpl, WidgetOutput};
/// An invisible widget that stores some arbitrary data on the Panel. Users of the panel can read
/// and write the value. This is one method for "returning" data when a State completes.
pub struct Stash<T> {
value: Rc<RefCell<T>>,
}
impl<T: 'static> Stash<T> {
pub fn new(name: &str, value: T) -> Widget {
Widget::new(Box::new(Stash {
value: Rc::new(RefCell::new(value)),
}))
.named(name)
}
pub(crate) fn get_value(&self) -> Rc<RefCell<T>> {
self.value.clone()
}
}
impl<T: 'static> WidgetImpl for Stash<T> {
fn get_dims(&self) -> ScreenDims {
ScreenDims::square(0.0)
}
fn set_pos(&mut self, _: ScreenPt) {}
fn event(&mut self, _: &mut EventCtx, _: &mut WidgetOutput) {}
fn draw(&self, _: &mut GfxCtx) {}
}