diff --git a/game/src/ungap/explore.rs b/game/src/ungap/explore.rs index 60b84a3e15..2de6161622 100644 --- a/game/src/ungap/explore.rs +++ b/game/src/ungap/explore.rs @@ -2,10 +2,7 @@ use geom::Distance; use map_gui::tools::URLManager; use map_gui::ID; use map_model::{EditCmd, LaneType}; -use widgetry::{ - lctrl, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, TextExt, - VerticalAlignment, Widget, -}; +use widgetry::{lctrl, EventCtx, GfxCtx, Key, Line, Outcome, Panel, State, TextExt, Widget}; use crate::app::{App, Transition}; use crate::edit::{LoadEdits, RoadEditor, SaveEdits}; @@ -207,10 +204,5 @@ fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel { } // TODO Should undo/redo, save, share functionality also live here? - Panel::new_builder(Widget::col(vec![ - Tab::Explore.make_header(ctx, app), - Widget::col(file_management).section(ctx), - ])) - .aligned(HorizontalAlignment::Left, VerticalAlignment::Top) - .build(ctx) + Tab::Explore.make_left_panel(ctx, app, Widget::col(file_management)) } diff --git a/game/src/ungap/mod.rs b/game/src/ungap/mod.rs index 8b989a4f68..d69886a395 100644 --- a/game/src/ungap/mod.rs +++ b/game/src/ungap/mod.rs @@ -10,7 +10,8 @@ mod share; use map_gui::tools::{grey_out_map, nice_map_name, open_browser, CityPicker}; use widgetry::{ - lctrl, EventCtx, GfxCtx, Key, Line, Panel, SimpleState, State, Text, TextExt, Widget, + lctrl, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Panel, SimpleState, State, Text, + TextExt, VerticalAlignment, Widget, }; pub use self::explore::ExploreMap; @@ -33,52 +34,86 @@ pub trait TakeLayers { } impl Tab { - pub fn make_header(self, ctx: &mut EventCtx, app: &App) -> Widget { - Widget::col(vec![ - Widget::row(vec![ - ctx.style() - .btn_plain - .btn() - .image_path("system/assets/pregame/logo.svg") - .image_dims(50.0) - .build_widget(ctx, "about A/B Street"), - ctx.style() - .btn_popup_icon_text( - "system/assets/tools/map.svg", - nice_map_name(app.primary.map.get_name()), - ) - .hotkey(lctrl(Key::L)) - .build_widget(ctx, "change map") - .centered_vert() - .align_right(), - ]), - Widget::row(vec![ - ctx.style() - .btn_tab - .icon_text("system/assets/tools/pan.svg", "Explore") - .hotkey(Key::Num1) - .disabled(self == Tab::Explore) - .build_def(ctx), - ctx.style() - .btn_tab - .icon_text("system/assets/tools/pencil.svg", "Create new bike lanes") - .hotkey(Key::Num2) - .disabled(self == Tab::Create) - .build_def(ctx), - ctx.style() - .btn_tab - .icon_text("system/assets/tools/pin.svg", "Plan a route") - .hotkey(Key::Num3) - .disabled(self == Tab::Route) - .build_def(ctx), - ctx.style() - .btn_tab - .icon_text("system/assets/meters/trip_histogram.svg", "Predict impact") - .hotkey(Key::Num4) - .disabled(self == Tab::PredictImpact) - .build_def(ctx), - ]), - ]) + pub fn make_left_panel(self, ctx: &mut EventCtx, app: &App, contents: Widget) -> Panel { + // Ideally TabController could manage this, but the contents of each section are + // substantial, controlled by entirely different States. + + let mut contents = Some(contents.section(ctx)); + + let mut col = vec![Widget::row(vec![ + ctx.style() + .btn_plain + .btn() + .image_path("system/assets/pregame/logo.svg") + .image_dims(50.0) + .build_widget(ctx, "about A/B Street"), + ctx.style() + .btn_popup_icon_text( + "system/assets/tools/map.svg", + nice_map_name(app.primary.map.get_name()), + ) + .hotkey(lctrl(Key::L)) + .build_widget(ctx, "change map") + .centered_vert() + .align_right(), + ])]; + + col.push( + ctx.style() + .btn_tab + .icon_text("system/assets/tools/pan.svg", "Explore") + .hotkey(Key::Num1) + .disabled(self == Tab::Explore) + .build_def(ctx), + ); + if self == Tab::Explore { + col.push(contents.take().unwrap()); + } + + col.push( + ctx.style() + .btn_tab + .icon_text("system/assets/tools/pencil.svg", "Create new bike lanes") + .hotkey(Key::Num2) + .disabled(self == Tab::Create) + .build_def(ctx), + ); + if self == Tab::Create { + col.push(contents.take().unwrap()); + } + + col.push( + ctx.style() + .btn_tab + .icon_text("system/assets/tools/pin.svg", "Plan a route") + .hotkey(Key::Num3) + .disabled(self == Tab::Route) + .build_def(ctx), + ); + if self == Tab::Route { + col.push(contents.take().unwrap()); + } + + col.push( + ctx.style() + .btn_tab + .icon_text("system/assets/meters/trip_histogram.svg", "Predict impact") + .hotkey(Key::Num4) + .disabled(self == Tab::PredictImpact) + .build_def(ctx), + ); + if self == Tab::PredictImpact { + col.push(contents.take().unwrap()); + } + + let mut panel = Panel::new_builder(Widget::col(col)) + .exact_height(ctx.canvas.window_height) + .aligned(HorizontalAlignment::Left, VerticalAlignment::Top); + if self == Tab::Route { + // Hovering on a card + panel = panel.ignore_initial_events(); + } + panel.build(ctx) } pub fn handle_action>( diff --git a/game/src/ungap/predict.rs b/game/src/ungap/predict.rs index a2e381111a..37d096fd2b 100644 --- a/game/src/ungap/predict.rs +++ b/game/src/ungap/predict.rs @@ -8,8 +8,7 @@ use map_gui::ID; use map_model::{PathRequest, PathStepV2, RoadID}; use sim::{Scenario, TripEndpoint, TripMode}; use widgetry::{ - Drawable, EventCtx, GfxCtx, HorizontalAlignment, Line, Outcome, Panel, Spinner, State, Text, - TextExt, VerticalAlignment, Widget, + Drawable, EventCtx, GfxCtx, Line, Outcome, Panel, Spinner, State, Text, TextExt, Widget, }; use crate::app::{App, Transition}; @@ -133,7 +132,6 @@ impl State for ShowGaps { fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel { let data = app.session.mode_shift.value().unwrap(); let col = vec![ - Tab::PredictImpact.make_header(ctx, app), // TODO Info button with popup explaining all the assumptions... (where scenario data comes // from, only driving -> cycling, no off-map starts or ends, etc) format!( @@ -155,9 +153,7 @@ fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel { data.results.describe().into_widget(ctx), ]; - Panel::new_builder(Widget::col(col)) - .aligned(HorizontalAlignment::Left, VerticalAlignment::Top) - .build(ctx) + Tab::PredictImpact.make_left_panel(ctx, app, Widget::col(col)) } // TODO For now, it's easier to just copy pieces from sandbox/dashboards/mode_shift.rs. I'm not diff --git a/game/src/ungap/quick_sketch.rs b/game/src/ungap/quick_sketch.rs index 5fadfa391e..af1ada5cb1 100644 --- a/game/src/ungap/quick_sketch.rs +++ b/game/src/ungap/quick_sketch.rs @@ -3,10 +3,7 @@ use map_gui::tools::PopupMsg; use map_model::{ BufferType, Direction, DrivingSide, EditCmd, EditRoad, LaneSpec, LaneType, RoadID, }; -use widgetry::{ - Choice, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, TextExt, - VerticalAlignment, Widget, -}; +use widgetry::{Choice, EventCtx, GfxCtx, Key, Outcome, Panel, State, TextExt, Widget}; use crate::app::{App, Transition}; use crate::common::RouteSketcher; @@ -37,10 +34,7 @@ impl QuickSketch { } fn update_top_panel(&mut self, ctx: &mut EventCtx, app: &App) { - let mut col = vec![ - Tab::Create.make_header(ctx, app), - self.route_sketcher.get_widget_to_describe(ctx), - ]; + let mut col = vec![self.route_sketcher.get_widget_to_describe(ctx)]; if self.route_sketcher.is_route_started() { // We're usually replacing an existing panel, except the very first time. @@ -78,9 +72,8 @@ impl QuickSketch { .evenly_spaced(), ); } - self.top_panel = Panel::new_builder(Widget::col(col)) - .aligned(HorizontalAlignment::Left, VerticalAlignment::Top) - .build(ctx); + + self.top_panel = Tab::Create.make_left_panel(ctx, app, Widget::col(col)); } } diff --git a/game/src/ungap/route.rs b/game/src/ungap/route.rs index 6869320f63..aa50c50842 100644 --- a/game/src/ungap/route.rs +++ b/game/src/ungap/route.rs @@ -8,9 +8,8 @@ use map_gui::tools::{grey_out_map, ChooseSomething, PopupMsg}; use map_model::{Path, PathStep, NORMAL_LANE_THICKNESS}; use sim::{TripEndpoint, TripMode}; use widgetry::{ - Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, LinePlot, - Outcome, Panel, PlotOptions, Series, SimpleState, Slider, State, Text, TextBox, TextExt, - VerticalAlignment, Widget, + Choice, Color, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Line, LinePlot, Outcome, Panel, + PlotOptions, Series, SimpleState, Slider, State, Text, TextBox, TextExt, Widget, }; use crate::app::{App, Transition}; @@ -55,8 +54,7 @@ impl RoutePlanner { self.results = results; let params = &app.session.routing_params; - let mut new_panel = Panel::new_builder(Widget::col(vec![ - Tab::Route.make_header(ctx, app), + let col = Widget::col(vec![ self.files.get_panel_widget(ctx), self.waypoints.get_panel_widget(ctx), Widget::col(vec![ @@ -81,11 +79,9 @@ impl RoutePlanner { ]) .section(ctx), results_widget, - ])) - .aligned(HorizontalAlignment::Left, VerticalAlignment::Top) - // Hovering on a card - .ignore_initial_events() - .build(ctx); + ]); + + let mut new_panel = Tab::Route.make_left_panel(ctx, app, col); // TODO After scrolling down and dragging a slider, sometimes releasing the slider // registers as clicking "X" on the waypoints! Maybe just replace() in that case?