mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
overhaul one of the oldest things ever, the almighty turn cycler
This commit is contained in:
parent
6e8414c088
commit
da6abc019b
@ -18,7 +18,6 @@ pub use self::warp::Warping;
|
||||
use crate::app::App;
|
||||
use crate::game::Transition;
|
||||
use crate::helpers::{list_names, ID};
|
||||
use crate::render::DrawOptions;
|
||||
use crate::sandbox::SpeedControls;
|
||||
use ezgui::{
|
||||
lctrl, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, ScreenDims, ScreenPt, ScreenRectangle,
|
||||
@ -280,14 +279,6 @@ impl CommonState {
|
||||
));
|
||||
}
|
||||
|
||||
pub fn draw_options(&self, app: &App) -> DrawOptions {
|
||||
let mut opts = DrawOptions::new();
|
||||
opts.suppress_traffic_signal_details = self
|
||||
.turn_cycler
|
||||
.suppress_traffic_signal_details(&app.primary.map);
|
||||
opts
|
||||
}
|
||||
|
||||
// Meant to be used for launching from other states
|
||||
pub fn launch_info_panel(&mut self, id: ID, ctx: &mut EventCtx, app: &mut App) {
|
||||
self.info_panel = Some(info::InfoPanel::new(id, ctx, app, Vec::new(), None));
|
||||
|
@ -1,41 +1,37 @@
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::colors;
|
||||
use crate::common::ColorLegend;
|
||||
use crate::game::{DrawBaselayer, State, Transition};
|
||||
use crate::helpers::ID;
|
||||
use crate::managed::WrappedComposite;
|
||||
use crate::render::{dashed_lines, draw_signal_phase, make_signal_diagram, DrawOptions, DrawTurn};
|
||||
use ezgui::{hotkey, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Outcome};
|
||||
use geom::{Distance, Time};
|
||||
use map_model::{IntersectionID, LaneID, Map, TurnType};
|
||||
use ezgui::{
|
||||
hotkey, Button, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment,
|
||||
Key, Line, ManagedWidget, Outcome, Text, VerticalAlignment,
|
||||
};
|
||||
use geom::{Distance, Polygon, Time};
|
||||
use map_model::{IntersectionID, LaneID, TurnType};
|
||||
use sim::{AgentID, DontDrawAgents};
|
||||
|
||||
// TODO Misnomer. Kind of just handles temporary hovering things now.
|
||||
pub enum TurnCyclerState {
|
||||
Inactive,
|
||||
ShowLane(LaneID),
|
||||
ShowRoute(AgentID, Time, Drawable),
|
||||
CycleTurns(LaneID, usize),
|
||||
}
|
||||
|
||||
impl TurnCyclerState {
|
||||
pub fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Option<Transition> {
|
||||
match app.primary.current_selection {
|
||||
Some(ID::Lane(id)) if !app.primary.map.get_turns_from_lane(id).is_empty() => {
|
||||
if let TurnCyclerState::CycleTurns(current, idx) = self {
|
||||
if *current != id {
|
||||
*self = TurnCyclerState::ShowLane(id);
|
||||
} else if app
|
||||
.per_obj
|
||||
.action(ctx, Key::Z, "cycle through this lane's turns")
|
||||
{
|
||||
*self = TurnCyclerState::CycleTurns(id, *idx + 1);
|
||||
}
|
||||
} else {
|
||||
*self = TurnCyclerState::ShowLane(id);
|
||||
if app
|
||||
.per_obj
|
||||
.action(ctx, Key::Z, "cycle through this lane's turns")
|
||||
{
|
||||
*self = TurnCyclerState::CycleTurns(id, 0);
|
||||
}
|
||||
if app
|
||||
.per_obj
|
||||
.action(ctx, Key::Z, "explore turns from this lane")
|
||||
{
|
||||
return Some(Transition::Push(Box::new(TurnExplorer {
|
||||
l: id,
|
||||
idx: 0,
|
||||
composite: TurnExplorer::make_panel(ctx, app, id, 0),
|
||||
})));
|
||||
}
|
||||
}
|
||||
Some(ID::Intersection(i)) => {
|
||||
@ -92,59 +88,14 @@ impl TurnCyclerState {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
pub fn draw(&self, g: &mut GfxCtx, _: &App) {
|
||||
match self {
|
||||
TurnCyclerState::Inactive => {}
|
||||
TurnCyclerState::ShowLane(l) => {
|
||||
for turn in &app.primary.map.get_turns_from_lane(*l) {
|
||||
DrawTurn::draw_full(turn, g, color_turn_type(turn.turn_type, app).alpha(0.5));
|
||||
}
|
||||
}
|
||||
TurnCyclerState::CycleTurns(l, idx) => {
|
||||
let turns = app.primary.map.get_turns_from_lane(*l);
|
||||
let current = turns[*idx % turns.len()];
|
||||
DrawTurn::draw_full(current, g, color_turn_type(current.turn_type, app));
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
for t in app.primary.map.get_turns_in_intersection(current.id.parent) {
|
||||
if current.conflicts_with(t) {
|
||||
DrawTurn::draw_dashed(
|
||||
t,
|
||||
&mut batch,
|
||||
app.cs.get_def("conflicting turn", Color::RED.alpha(0.8)),
|
||||
);
|
||||
}
|
||||
}
|
||||
batch.draw(g);
|
||||
}
|
||||
TurnCyclerState::ShowRoute(_, _, ref d) => {
|
||||
g.redraw(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suppress_traffic_signal_details(&self, map: &Map) -> Option<IntersectionID> {
|
||||
match self {
|
||||
TurnCyclerState::ShowLane(l) | TurnCyclerState::CycleTurns(l, _) => {
|
||||
Some(map.get_l(*l).dst_i)
|
||||
}
|
||||
TurnCyclerState::ShowRoute(_, _, _) | TurnCyclerState::Inactive => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn color_turn_type(t: TurnType, app: &App) -> Color {
|
||||
match t {
|
||||
TurnType::SharedSidewalkCorner => {
|
||||
app.cs.get_def("shared sidewalk corner turn", Color::BLACK)
|
||||
}
|
||||
TurnType::Crosswalk => app.cs.get_def("crosswalk turn", Color::WHITE),
|
||||
TurnType::Straight => app.cs.get_def("straight turn", Color::BLUE),
|
||||
TurnType::LaneChangeLeft => app.cs.get_def("change lanes left turn", Color::CYAN),
|
||||
TurnType::LaneChangeRight => app.cs.get_def("change lanes right turn", Color::PURPLE),
|
||||
TurnType::Right => app.cs.get_def("right turn", Color::GREEN),
|
||||
TurnType::Left => app.cs.get_def("left turn", Color::RED),
|
||||
}
|
||||
}
|
||||
|
||||
struct ShowTrafficSignal {
|
||||
@ -189,7 +140,7 @@ impl State for ShowTrafficSignal {
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
let mut opts = DrawOptions::new();
|
||||
opts.suppress_traffic_signal_details = Some(self.i);
|
||||
opts.suppress_traffic_signal_details.push(self.i);
|
||||
app.draw(g, opts, &DontDrawAgents {}, &ShowEverything::new());
|
||||
let mut batch = GeomBatch::new();
|
||||
draw_signal_phase(
|
||||
@ -217,3 +168,196 @@ impl ShowTrafficSignal {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TurnExplorer {
|
||||
l: LaneID,
|
||||
// 0 means all turns, otherwise one particular turn
|
||||
idx: usize,
|
||||
composite: Composite,
|
||||
}
|
||||
|
||||
impl State for TurnExplorer {
|
||||
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||
ctx.canvas_movement();
|
||||
|
||||
match self.composite.event(ctx) {
|
||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||
"X" => {
|
||||
return Transition::Pop;
|
||||
}
|
||||
"previous turn" => {
|
||||
if self.idx != 0 {
|
||||
self.idx -= 1;
|
||||
self.composite = TurnExplorer::make_panel(ctx, app, self.l, self.idx);
|
||||
}
|
||||
}
|
||||
"next turn" => {
|
||||
if self.idx != app.primary.map.get_turns_from_lane(self.l).len() {
|
||||
self.idx += 1;
|
||||
self.composite = TurnExplorer::make_panel(ctx, app, self.l, self.idx);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw_baselayer(&self) -> DrawBaselayer {
|
||||
DrawBaselayer::Custom
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
let mut opts = DrawOptions::new();
|
||||
{
|
||||
let l = app.primary.map.get_l(self.l);
|
||||
opts.suppress_traffic_signal_details.push(l.src_i);
|
||||
opts.suppress_traffic_signal_details.push(l.dst_i);
|
||||
}
|
||||
app.draw(g, opts, &DontDrawAgents {}, &ShowEverything::new());
|
||||
|
||||
if self.idx == 0 {
|
||||
for turn in &app.primary.map.get_turns_from_lane(self.l) {
|
||||
DrawTurn::draw_full(turn, g, color_turn_type(turn.turn_type, app).alpha(0.5));
|
||||
}
|
||||
} else {
|
||||
let current = &app.primary.map.get_turns_from_lane(self.l)[self.idx - 1];
|
||||
DrawTurn::draw_full(current, g, app.cs.get_def("current turn", Color::GREEN));
|
||||
|
||||
let mut batch = GeomBatch::new();
|
||||
for t in app.primary.map.get_turns_in_intersection(current.id.parent) {
|
||||
if current.conflicts_with(t) {
|
||||
DrawTurn::draw_dashed(
|
||||
t,
|
||||
&mut batch,
|
||||
app.cs.get_def("conflicting turn", Color::RED.alpha(0.8)),
|
||||
);
|
||||
}
|
||||
}
|
||||
batch.draw(g);
|
||||
}
|
||||
|
||||
self.composite.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
impl TurnExplorer {
|
||||
fn make_panel(ctx: &mut EventCtx, app: &App, l: LaneID, idx: usize) -> Composite {
|
||||
let num_turns = app.primary.map.get_turns_from_lane(l).len();
|
||||
|
||||
let mut col = vec![ManagedWidget::row(vec![
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(
|
||||
Line(format!(
|
||||
"Turns from {}",
|
||||
app.primary.map.get_parent(l).get_name()
|
||||
))
|
||||
.size(26),
|
||||
),
|
||||
)
|
||||
.margin(5),
|
||||
ManagedWidget::draw_batch(
|
||||
ctx,
|
||||
GeomBatch::from(vec![(Color::WHITE, Polygon::rectangle(2.0, 50.0))]),
|
||||
)
|
||||
.margin(5),
|
||||
ManagedWidget::draw_text(
|
||||
ctx,
|
||||
Text::from(Line(format!("{}/{}", idx, num_turns)).size(20)),
|
||||
)
|
||||
.margin(5)
|
||||
.centered_vert(),
|
||||
if idx == 0 {
|
||||
Button::inactive_button(ctx, "<")
|
||||
} else {
|
||||
WrappedComposite::nice_text_button(
|
||||
ctx,
|
||||
Text::from(Line("<")),
|
||||
hotkey(Key::LeftArrow),
|
||||
"previous turn",
|
||||
)
|
||||
}
|
||||
.margin(5),
|
||||
if idx == num_turns {
|
||||
Button::inactive_button(ctx, ">")
|
||||
} else {
|
||||
WrappedComposite::nice_text_button(
|
||||
ctx,
|
||||
Text::from(Line(">")),
|
||||
hotkey(Key::RightArrow),
|
||||
"next turn",
|
||||
)
|
||||
}
|
||||
.margin(5),
|
||||
WrappedComposite::text_button(ctx, "X", hotkey(Key::Escape)),
|
||||
])];
|
||||
if idx == 0 {
|
||||
if app.primary.map.get_l(l).is_sidewalk() {
|
||||
col.push(ColorLegend::row(
|
||||
ctx,
|
||||
app.cs.get("crosswalk turn"),
|
||||
"crosswalk",
|
||||
));
|
||||
col.push(ColorLegend::row(
|
||||
ctx,
|
||||
app.cs.get("shared sidewalk corner turn"),
|
||||
"sidewalk connection",
|
||||
));
|
||||
} else {
|
||||
col.push(ColorLegend::row(
|
||||
ctx,
|
||||
app.cs.get("straight turn"),
|
||||
"straight",
|
||||
));
|
||||
col.push(ColorLegend::row(
|
||||
ctx,
|
||||
app.cs.get("right turn"),
|
||||
"right turn",
|
||||
));
|
||||
col.push(ColorLegend::row(ctx, app.cs.get("left turn"), "left turn"));
|
||||
col.push(ColorLegend::row(
|
||||
ctx,
|
||||
app.cs.get("change lanes left turn"),
|
||||
"straight, but lane-change left",
|
||||
));
|
||||
col.push(ColorLegend::row(
|
||||
ctx,
|
||||
app.cs.get("change lanes right turn"),
|
||||
"straight, but lane-change right",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
col.push(ColorLegend::row(
|
||||
ctx,
|
||||
app.cs.get("current turn"),
|
||||
"current turn",
|
||||
));
|
||||
col.push(ColorLegend::row(
|
||||
ctx,
|
||||
app.cs.get("conflicting turn"),
|
||||
"conflicting turn",
|
||||
));
|
||||
}
|
||||
|
||||
Composite::new(ManagedWidget::col(col).bg(colors::PANEL_BG))
|
||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
||||
.build(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
fn color_turn_type(t: TurnType, app: &App) -> Color {
|
||||
match t {
|
||||
TurnType::SharedSidewalkCorner => {
|
||||
app.cs.get_def("shared sidewalk corner turn", Color::BLACK)
|
||||
}
|
||||
TurnType::Crosswalk => app.cs.get_def("crosswalk turn", Color::WHITE),
|
||||
TurnType::Straight => app.cs.get_def("straight turn", Color::BLUE),
|
||||
TurnType::LaneChangeLeft => app.cs.get_def("change lanes left turn", Color::CYAN),
|
||||
TurnType::LaneChangeRight => app.cs.get_def("change lanes right turn", Color::PURPLE),
|
||||
TurnType::Right => app.cs.get_def("right turn", Color::GREEN),
|
||||
TurnType::Left => app.cs.get_def("left turn", Color::RED),
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use crate::common::{tool_panel, CommonState};
|
||||
use crate::game::{msg, DrawBaselayer, State, Transition, WizardState};
|
||||
use crate::helpers::ID;
|
||||
use crate::managed::{WrappedComposite, WrappedOutcome};
|
||||
use crate::render::DrawOptions;
|
||||
use ezgui::{
|
||||
hotkey, lctrl, Color, Composite, Drawable, EventCtx, EventLoopMode, GeomBatch, GfxCtx,
|
||||
HorizontalAlignment, Key, Line, ManagedWidget, Outcome, Text, VerticalAlignment, Wizard,
|
||||
@ -328,7 +329,7 @@ impl State for DebugMode {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
let mut opts = self.common.draw_options(app);
|
||||
let mut opts = DrawOptions::new();
|
||||
opts.label_buildings = self.layers.show_labels;
|
||||
opts.label_roads = self.layers.show_labels;
|
||||
app.draw(g, opts, &app.primary.sim, self);
|
||||
|
@ -230,7 +230,7 @@ impl State for TrafficSignalEditor {
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
{
|
||||
let mut opts = DrawOptions::new();
|
||||
opts.suppress_traffic_signal_details = Some(self.i);
|
||||
opts.suppress_traffic_signal_details.push(self.i);
|
||||
app.draw(g, opts, &app.primary.sim, &ShowEverything::new());
|
||||
}
|
||||
|
||||
|
@ -124,7 +124,7 @@ impl Renderable for DrawIntersection {
|
||||
g.redraw(&self.draw_default);
|
||||
|
||||
if self.intersection_type == IntersectionType::TrafficSignal
|
||||
&& opts.suppress_traffic_signal_details != Some(self.id)
|
||||
&& !opts.suppress_traffic_signal_details.contains(&self.id)
|
||||
{
|
||||
let signal = app.primary.map.get_traffic_signal(self.id);
|
||||
let mut maybe_redraw = self.draw_traffic_signal.borrow_mut();
|
||||
|
@ -91,7 +91,7 @@ pub fn dashed_lines(
|
||||
// DrawOptions.
|
||||
#[derive(Clone)]
|
||||
pub struct DrawOptions {
|
||||
pub suppress_traffic_signal_details: Option<IntersectionID>,
|
||||
pub suppress_traffic_signal_details: Vec<IntersectionID>,
|
||||
pub label_buildings: bool,
|
||||
pub label_roads: bool,
|
||||
}
|
||||
@ -99,7 +99,7 @@ pub struct DrawOptions {
|
||||
impl DrawOptions {
|
||||
pub fn new() -> DrawOptions {
|
||||
DrawOptions {
|
||||
suppress_traffic_signal_details: None,
|
||||
suppress_traffic_signal_details: Vec::new(),
|
||||
label_buildings: false,
|
||||
label_roads: false,
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ mod dashboards;
|
||||
mod gameplay;
|
||||
mod speed;
|
||||
|
||||
use crate::app::{App, ShowEverything};
|
||||
use crate::app::App;
|
||||
use crate::colors;
|
||||
use crate::common::{tool_panel, CommonState, Minimap, Overlays, ShowBusRoute};
|
||||
use crate::debug::DebugMode;
|
||||
@ -10,11 +10,11 @@ use crate::edit::{
|
||||
apply_map_edits, can_edit_lane, save_edits_as, EditMode, LaneEditor, StopSignEditor,
|
||||
TrafficSignalEditor,
|
||||
};
|
||||
use crate::game::{DrawBaselayer, State, Transition, WizardState};
|
||||
use crate::game::{State, Transition, WizardState};
|
||||
use crate::helpers::{cmp_duration_shorter, ID};
|
||||
use crate::managed::{WrappedComposite, WrappedOutcome};
|
||||
use crate::pregame::main_menu;
|
||||
use crate::render::{AgentColorScheme, DrawOptions};
|
||||
use crate::render::AgentColorScheme;
|
||||
pub use crate::sandbox::gameplay::{TutorialPointer, TutorialState};
|
||||
use ezgui::{
|
||||
hotkey, lctrl, Choice, Color, Composite, EventCtx, EventLoopMode, GeomBatch, GfxCtx,
|
||||
@ -266,21 +266,7 @@ impl State for SandboxMode {
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_baselayer(&self) -> DrawBaselayer {
|
||||
DrawBaselayer::Custom
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
app.draw(
|
||||
g,
|
||||
self.controls
|
||||
.common
|
||||
.as_ref()
|
||||
.map(|c| c.draw_options(app))
|
||||
.unwrap_or_else(DrawOptions::new),
|
||||
&app.primary.sim,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
app.overlay.draw(g);
|
||||
|
||||
if let Some(ref c) = self.controls.common {
|
||||
|
Loading…
Reference in New Issue
Block a user