From 6de415033ec74e67c6795ffe8bd37ede82ff408b Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Tue, 17 Mar 2020 09:46:51 -0700 Subject: [PATCH] more button refactor: - custom tooltips - get rid of a few old Button constructors --- ezgui/src/drawing.rs | 10 ++ ezgui/src/widgets/button.rs | 185 +++++++++++---------- game/src/common/info.rs | 256 ++++++++++++++---------------- game/src/common/minimap.rs | 48 +++--- game/src/render/traffic_signal.rs | 14 +- game/src/sandbox/speed.rs | 42 ++--- 6 files changed, 284 insertions(+), 271 deletions(-) diff --git a/ezgui/src/drawing.rs b/ezgui/src/drawing.rs index 8c34e0fcf5..1a25472234 100644 --- a/ezgui/src/drawing.rs +++ b/ezgui/src/drawing.rs @@ -389,6 +389,16 @@ impl GeomBatch { } } + pub fn from_svg>( + 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, diff --git a/ezgui/src/widgets/button.rs b/ezgui/src/widgets/button.rs index 4192453907..a6a117c1af 100644 --- a/ezgui/src/widgets/button.rs +++ b/ezgui/src/widgets/button.rs @@ -1,5 +1,4 @@ use crate::layout::Widget; -use crate::svg; use crate::{ text, Color, Drawable, EventCtx, GeomBatch, GfxCtx, JustDraw, Line, ManagedWidget, MultiKey, RewriteColor, ScreenDims, ScreenPt, Text, @@ -27,7 +26,7 @@ pub struct Button { } impl Button { - pub fn new( + fn new( ctx: &EventCtx, normal: GeomBatch, hovered: GeomBatch, @@ -62,11 +61,6 @@ impl Button { } } - pub fn change_tooltip(mut self, tooltip: Text) -> Button { - self.tooltip = tooltip; - self - } - pub(crate) fn event(&mut self, ctx: &mut EventCtx) { if self.clicked { panic!("Caller didn't consume button click"); @@ -128,38 +122,6 @@ impl Widget for Button { // TODO Simplify all of these APIs! impl Button { - pub fn rectangle_svg( - filename: &str, - tooltip: &str, - key: Option, - hover: RewriteColor, - ctx: &EventCtx, - ) -> Button { - let (normal, bounds) = svg::load_svg(ctx.prerender, filename); - - let mut hovered = normal.clone(); - hovered.rewrite_color(hover); - - Button::new(ctx, normal, hovered, key, tooltip, bounds.get_rectangle()) - } - - pub fn rectangle_svg_rewrite( - filename: &str, - tooltip: &str, - key: Option, - normal_rewrite: RewriteColor, - hover: RewriteColor, - ctx: &EventCtx, - ) -> Button { - let (mut normal, bounds) = svg::load_svg(ctx.prerender, filename); - normal.rewrite_color(normal_rewrite); - - let mut hovered = normal.clone(); - hovered.rewrite_color(hover); - - Button::new(ctx, normal, hovered, key, tooltip, bounds.get_rectangle()) - } - pub fn text_bg( text: Text, unselected_bg_color: Color, @@ -266,33 +228,52 @@ pub struct Btn {} impl Btn { pub fn svg>(path: I, hover: RewriteColor) -> BtnBuilder { - BtnBuilder::SVG(path.into(), hover) + BtnBuilder::SVG(path.into(), hover, None) } // Same as WrappedComposite::text_button pub fn text_fg>(label: I) -> BtnBuilder { - BtnBuilder::TextFG(label.into()) + BtnBuilder::TextFG(label.into(), None) } // The info panel style with the lighter background color pub fn text_bg1>(label: I) -> BtnBuilder { - BtnBuilder::TextBG1(label.into()) + BtnBuilder::TextBG1(label.into(), None) } // The white background. WrappedComposite::text_bg_button. pub fn text_bg2>(label: I) -> BtnBuilder { - BtnBuilder::TextBG2(label.into()) + BtnBuilder::TextBG2(label.into(), None) + } + + pub fn custom(normal: GeomBatch, hovered: GeomBatch, hitbox: Polygon) -> BtnBuilder { + BtnBuilder::Custom(normal, hovered, hitbox, None) } } pub enum BtnBuilder { - SVG(String, RewriteColor), - TextFG(String), - TextBG1(String), - TextBG2(String), + SVG(String, RewriteColor, Option), + TextFG(String, Option), + TextBG1(String, Option), + TextBG2(String, Option), + Custom(GeomBatch, GeomBatch, Polygon, Option), } impl BtnBuilder { + pub fn tooltip(mut self, tooltip: Text) -> BtnBuilder { + match self { + BtnBuilder::SVG(_, _, ref mut t) + | BtnBuilder::TextFG(_, ref mut t) + | BtnBuilder::TextBG1(_, ref mut t) + | BtnBuilder::TextBG2(_, ref mut t) + | BtnBuilder::Custom(_, _, _, ref mut t) => { + assert!(t.is_none()); + *t = Some(tooltip); + } + } + self + } + pub fn build>( self, ctx: &EventCtx, @@ -300,48 +281,86 @@ impl BtnBuilder { key: Option, ) -> ManagedWidget { match self { - BtnBuilder::SVG(path, hover) => ManagedWidget::btn(Button::rectangle_svg( - &path, - &action_tooltip.into(), - key, - hover, - ctx, - )), - BtnBuilder::TextFG(label) => ManagedWidget::btn(Button::text_no_bg( - Text::from(Line(&label)), - Text::from(Line(label).fg(Color::ORANGE)), - key, - &action_tooltip.into(), - true, - ctx, - )) - .outline(2.0, Color::WHITE), - BtnBuilder::TextBG1(label) => ManagedWidget::btn(Button::text_bg( - Text::from(Line(label)), - Color::grey(0.5), - Color::ORANGE, - key, - &action_tooltip.into(), - ctx, - )), - BtnBuilder::TextBG2(label) => ManagedWidget::btn(Button::text_bg( - Text::from(Line(label).fg(Color::BLACK)), - Color::WHITE, - Color::ORANGE, - key, - &action_tooltip.into(), - ctx, - )), + BtnBuilder::SVG(path, hover, maybe_t) => { + let (normal, bounds) = GeomBatch::from_svg(ctx, path, RewriteColor::NoOp); + + let mut hovered = normal.clone(); + hovered.rewrite_color(hover); + + let mut btn = Button::new( + ctx, + normal, + hovered, + key, + &action_tooltip.into(), + bounds.get_rectangle(), + ); + if let Some(t) = maybe_t { + btn.tooltip = t; + } + ManagedWidget::btn(btn) + } + BtnBuilder::TextFG(label, maybe_t) => { + let mut btn = Button::text_no_bg( + Text::from(Line(&label)), + Text::from(Line(label).fg(Color::ORANGE)), + key, + &action_tooltip.into(), + true, + ctx, + ); + if let Some(t) = maybe_t { + btn.tooltip = t; + } + ManagedWidget::btn(btn).outline(2.0, Color::WHITE) + } + BtnBuilder::TextBG1(label, maybe_t) => { + let mut btn = Button::text_bg( + Text::from(Line(label)), + Color::grey(0.5), + Color::ORANGE, + key, + &action_tooltip.into(), + ctx, + ); + if let Some(t) = maybe_t { + btn.tooltip = t; + } + ManagedWidget::btn(btn) + } + BtnBuilder::TextBG2(label, maybe_t) => { + let mut btn = Button::text_bg( + Text::from(Line(label).fg(Color::BLACK)), + Color::WHITE, + Color::ORANGE, + key, + &action_tooltip.into(), + ctx, + ); + if let Some(t) = maybe_t { + btn.tooltip = t; + } + ManagedWidget::btn(btn) + } + BtnBuilder::Custom(normal, hovered, hitbox, maybe_t) => { + let mut btn = + Button::new(ctx, normal, hovered, key, &action_tooltip.into(), hitbox); + if let Some(t) = maybe_t { + btn.tooltip = t; + } + ManagedWidget::btn(btn) + } } } // Use the text as the action pub fn build_def(self, ctx: &EventCtx, hotkey: Option) -> ManagedWidget { match self { - BtnBuilder::SVG(_, _) => panic!("Can't use build_def on an SVG button"), - BtnBuilder::TextFG(ref label) - | BtnBuilder::TextBG1(ref label) - | BtnBuilder::TextBG2(ref label) => { + BtnBuilder::SVG(_, _, _) => panic!("Can't use build_def on an SVG button"), + BtnBuilder::Custom(_, _, _, _) => panic!("Can't use build_def on a custom button"), + BtnBuilder::TextFG(ref label, _) + | BtnBuilder::TextBG1(ref label, _) + | BtnBuilder::TextBG2(ref label, _) => { let copy = label.clone(); self.build(ctx, copy, hotkey) } diff --git a/game/src/common/info.rs b/game/src/common/info.rs index d09d4d91d0..bd6ddb7e5d 100644 --- a/game/src/common/info.rs +++ b/game/src/common/info.rs @@ -984,136 +984,131 @@ fn trip_details( let mut zoomed = GeomBatch::new(); let mut markers = HashMap::new(); - let mut start_btn = Button::rectangle_svg( - "../data/system/assets/timeline/start_pos.svg", - "jump to start", - None, - RewriteColor::Change(Color::WHITE, colors::HOVERING), - ctx, - ); - let mut goal_btn = Button::rectangle_svg( - "../data/system/assets/timeline/goal_pos.svg", - "jump to goal", - None, - RewriteColor::Change(Color::WHITE, colors::HOVERING), - ctx, - ); - let trip_start_time = phases[0].start_time; let trip_end_time = phases.last().as_ref().and_then(|p| p.end_time); - // Start - { - match trip_start { - TripStart::Bldg(b) => { - let bldg = map.get_b(b); + let start_tooltip = match trip_start { + TripStart::Bldg(b) => { + let bldg = map.get_b(b); - let mut txt = Text::from(Line("jump to start")); - txt.add(Line(bldg.just_address(map))); - txt.add(Line(phases[0].start_time.ampm_tostring())); - start_btn = start_btn.change_tooltip(txt); - markers.insert("jump to start".to_string(), ID::Building(b)); + markers.insert("jump to start".to_string(), ID::Building(b)); - unzoomed.add_svg( - ctx.prerender, - "../data/system/assets/timeline/start_pos.svg", - bldg.label_center, - 1.0, - Angle::ZERO, - ); - zoomed.add_svg( - ctx.prerender, - "../data/system/assets/timeline/start_pos.svg", - bldg.label_center, - 0.5, - Angle::ZERO, - ); + unzoomed.add_svg( + ctx.prerender, + "../data/system/assets/timeline/start_pos.svg", + bldg.label_center, + 1.0, + Angle::ZERO, + ); + zoomed.add_svg( + ctx.prerender, + "../data/system/assets/timeline/start_pos.svg", + bldg.label_center, + 0.5, + Angle::ZERO, + ); + + let mut txt = Text::from(Line("jump to start")); + txt.add(Line(bldg.just_address(map))); + txt.add(Line(phases[0].start_time.ampm_tostring())); + txt + } + TripStart::Border(i) => { + let i = map.get_i(i); + + markers.insert("jump to start".to_string(), ID::Intersection(i.id)); + + unzoomed.add_svg( + ctx.prerender, + "../data/system/assets/timeline/start_pos.svg", + i.polygon.center(), + 1.0, + Angle::ZERO, + ); + zoomed.add_svg( + ctx.prerender, + "../data/system/assets/timeline/start_pos.svg", + i.polygon.center(), + 0.5, + Angle::ZERO, + ); + + let mut txt = Text::from(Line("jump to start")); + txt.add(Line(i.name(map))); + txt.add(Line(phases[0].start_time.ampm_tostring())); + txt + } + }; + let start_btn = Btn::svg( + "../data/system/assets/timeline/start_pos.svg", + RewriteColor::Change(Color::WHITE, colors::HOVERING), + ) + .tooltip(start_tooltip) + .build(ctx, "jump to start", None); + + let goal_tooltip = match trip_end { + TripEnd::Bldg(b) => { + let bldg = map.get_b(b); + + markers.insert("jump to goal".to_string(), ID::Building(b)); + + unzoomed.add_svg( + ctx.prerender, + "../data/system/assets/timeline/goal_pos.svg", + bldg.label_center, + 1.0, + Angle::ZERO, + ); + zoomed.add_svg( + ctx.prerender, + "../data/system/assets/timeline/goal_pos.svg", + bldg.label_center, + 0.5, + Angle::ZERO, + ); + + let mut txt = Text::from(Line("jump to goal")); + txt.add(Line(bldg.just_address(map))); + if let Some(t) = trip_end_time { + txt.add(Line(t.ampm_tostring())); } - TripStart::Border(i) => { - let i = map.get_i(i); + txt + } + TripEnd::Border(i) => { + let i = map.get_i(i); - let mut txt = Text::from(Line("jump to start")); - txt.add(Line(i.name(map))); - txt.add(Line(phases[0].start_time.ampm_tostring())); - start_btn = start_btn.change_tooltip(txt); - markers.insert("jump to start".to_string(), ID::Intersection(i.id)); + markers.insert("jump to goal".to_string(), ID::Intersection(i.id)); - unzoomed.add_svg( - ctx.prerender, - "../data/system/assets/timeline/start_pos.svg", - i.polygon.center(), - 1.0, - Angle::ZERO, - ); - zoomed.add_svg( - ctx.prerender, - "../data/system/assets/timeline/start_pos.svg", - i.polygon.center(), - 0.5, - Angle::ZERO, - ); + unzoomed.add_svg( + ctx.prerender, + "../data/system/assets/timeline/goal_pos.svg", + i.polygon.center(), + 1.0, + Angle::ZERO, + ); + zoomed.add_svg( + ctx.prerender, + "../data/system/assets/timeline/goal_pos.svg", + i.polygon.center(), + 0.5, + Angle::ZERO, + ); + + let mut txt = Text::from(Line("jump to goal")); + txt.add(Line(i.name(map))); + if let Some(t) = trip_end_time { + txt.add(Line(t.ampm_tostring())); } - }; - } - - // Goal - { - match trip_end { - TripEnd::Bldg(b) => { - let bldg = map.get_b(b); - - let mut txt = Text::from(Line("jump to goal")); - txt.add(Line(bldg.just_address(map))); - if let Some(t) = trip_end_time { - txt.add(Line(t.ampm_tostring())); - } - goal_btn = goal_btn.change_tooltip(txt); - markers.insert("jump to goal".to_string(), ID::Building(b)); - - unzoomed.add_svg( - ctx.prerender, - "../data/system/assets/timeline/goal_pos.svg", - bldg.label_center, - 1.0, - Angle::ZERO, - ); - zoomed.add_svg( - ctx.prerender, - "../data/system/assets/timeline/goal_pos.svg", - bldg.label_center, - 0.5, - Angle::ZERO, - ); - } - TripEnd::Border(i) => { - let i = map.get_i(i); - - let mut txt = Text::from(Line("jump to goal")); - txt.add(Line(i.name(map))); - if let Some(t) = trip_end_time { - txt.add(Line(t.ampm_tostring())); - } - goal_btn = goal_btn.change_tooltip(txt); - markers.insert("jump to goal".to_string(), ID::Intersection(i.id)); - - unzoomed.add_svg( - ctx.prerender, - "../data/system/assets/timeline/goal_pos.svg", - i.polygon.center(), - 1.0, - Angle::ZERO, - ); - zoomed.add_svg( - ctx.prerender, - "../data/system/assets/timeline/goal_pos.svg", - i.polygon.center(), - 0.5, - Angle::ZERO, - ); - } - TripEnd::ServeBusRoute(_) => unreachable!(), - }; - } + txt + } + TripEnd::ServeBusRoute(_) => unreachable!(), + }; + let goal_btn = Btn::svg( + "../data/system/assets/timeline/goal_pos.svg", + RewriteColor::Change(Color::WHITE, colors::HOVERING), + ) + .tooltip(goal_tooltip) + .build(ctx, "jump to goal", None); let total_duration_so_far = trip_end_time.unwrap_or_else(|| app.primary.sim.time()) - phases[0].start_time; @@ -1194,18 +1189,9 @@ fn trip_details( } timeline.push( - ManagedWidget::btn( - Button::new( - ctx, - normal, - hovered, - None, - &format!("examine trip phase {}", idx + 1), - rect, - ) - .change_tooltip(txt), - ) - .centered_vert(), + Btn::custom(normal, hovered, rect) + .build(ctx, format!("examine trip phase {}", idx + 1), None) + .centered_vert(), ); // TODO Could really cache this between live updates @@ -1237,8 +1223,8 @@ fn trip_details( } } - timeline.insert(0, ManagedWidget::btn(start_btn).margin(5)); - timeline.push(ManagedWidget::btn(goal_btn).margin(5)); + timeline.insert(0, start_btn.margin(5)); + timeline.push(goal_btn.margin(5)); let mut table = vec![ ("Trip start".to_string(), trip_start_time.ampm_tostring()), diff --git a/game/src/common/minimap.rs b/game/src/common/minimap.rs index 2f81e95a46..761256088e 100644 --- a/game/src/common/minimap.rs +++ b/game/src/common/minimap.rs @@ -6,7 +6,7 @@ use crate::managed::WrappedComposite; use crate::render::{AgentColorScheme, MIN_ZOOM_FOR_DETAIL}; use abstutil::clamp; use ezgui::{ - hotkey, Btn, Button, Choice, Color, Composite, EventCtx, Filler, GeomBatch, GfxCtx, + hotkey, Btn, Choice, Color, Composite, EventCtx, Filler, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, ManagedWidget, Outcome, RewriteColor, ScreenDims, ScreenPt, Text, VerticalAlignment, }; @@ -294,14 +294,14 @@ fn make_minimap_panel(ctx: &mut EventCtx, acs: &AgentColorScheme, zoom_lvl: usiz Color::WHITE }; let rect = Polygon::rectangle(20.0, 8.0); - zoom_col.push(ManagedWidget::btn(Button::new( - ctx, - GeomBatch::from(vec![(color, rect.clone())]), - GeomBatch::from(vec![(colors::HOVERING, rect.clone())]), - None, - &format!("zoom to level {}", i + 1), - rect, - ))); + zoom_col.push( + Btn::custom( + GeomBatch::from(vec![(color, rect.clone())]), + GeomBatch::from(vec![(colors::HOVERING, rect.clone())]), + rect, + ) + .build(ctx, format!("zoom to level {}", i + 1), None), + ); } zoom_col.push( Btn::svg( @@ -422,19 +422,23 @@ fn make_viz_panel(ctx: &mut EventCtx, acs: &AgentColorScheme) -> ManagedWidget { for (label, color, enabled) in &acs.rows { col.push( ManagedWidget::row(vec![ - ManagedWidget::btn(Button::rectangle_svg_rewrite( - "../data/system/assets/tools/visibility.svg", - &format!("show/hide {}", label), - None, - if *enabled { - RewriteColor::NoOp - } else { - RewriteColor::ChangeAll(Color::WHITE.alpha(0.5)) - }, - RewriteColor::ChangeAll(colors::HOVERING), - ctx, - )) - .margin(3), + { + let (normal, bounds) = GeomBatch::from_svg( + ctx, + "../data/system/assets/tools/visibility.svg", + if *enabled { + RewriteColor::NoOp + } else { + RewriteColor::ChangeAll(Color::WHITE.alpha(0.5)) + }, + ); + let mut hovered = normal.clone(); + hovered.rewrite_color(RewriteColor::ChangeAll(colors::HOVERING)); + + Btn::custom(normal, hovered, bounds.get_rectangle()) + .build(ctx, format!("show/hide {}", label), None) + .margin(3) + }, ManagedWidget::draw_batch( ctx, GeomBatch::from(vec![( diff --git a/game/src/render/traffic_signal.rs b/game/src/render/traffic_signal.rs index 178be612d3..102bebd99e 100644 --- a/game/src/render/traffic_signal.rs +++ b/game/src/render/traffic_signal.rs @@ -5,7 +5,7 @@ use crate::options::TrafficSignalStyle; use crate::render::intersection::make_crosswalk; use crate::render::{DrawTurnGroup, BIG_ARROW_THICKNESS}; use ezgui::{ - hotkey, Button, Color, Composite, EventCtx, GeomBatch, HorizontalAlignment, Key, Line, + hotkey, Btn, Color, Composite, EventCtx, GeomBatch, HorizontalAlignment, Key, Line, ManagedWidget, Prerender, Text, TextExt, VerticalAlignment, }; use geom::{Angle, Circle, Distance, Duration, Line, PolyLine, Polygon, Pt2D}; @@ -316,15 +316,9 @@ pub fn make_signal_diagram( hovered.push(Color::RED, bbox.to_outline(Distance::meters(5.0))); phase_rows.push( - ManagedWidget::btn(Button::new( - ctx, - normal, - hovered, - None, - &format!("phase {}", idx + 1), - bbox.clone(), - )) - .margin(5), + Btn::custom(normal, hovered, bbox.clone()) + .build(ctx, format!("phase {}", idx + 1), None) + .margin(5), ); if idx == selected { diff --git a/game/src/sandbox/speed.rs b/game/src/sandbox/speed.rs index d11c55b4ac..1b659e21a3 100644 --- a/game/src/sandbox/speed.rs +++ b/game/src/sandbox/speed.rs @@ -65,28 +65,28 @@ impl SpeedControls { ] .into_iter() .map(|(s, label)| { - let mut tooltip = Text::from(Line(label).size(20)); - tooltip.add(Line(Key::LeftArrow.describe()).fg(Color::GREEN).size(20)); - tooltip.append(Line(" - slow down")); - tooltip.add(Line(Key::RightArrow.describe()).fg(Color::GREEN).size(20)); - tooltip.append(Line(" - speed up")); + let mut txt = Text::from(Line(label).size(20)); + txt.add(Line(Key::LeftArrow.describe()).fg(Color::GREEN).size(20)); + txt.append(Line(" - slow down")); + txt.add(Line(Key::RightArrow.describe()).fg(Color::GREEN).size(20)); + txt.append(Line(" - speed up")); - ManagedWidget::btn( - Button::rectangle_svg_rewrite( - "../data/system/assets/speed/triangle.svg", - label, - None, - if setting >= s { - RewriteColor::NoOp - } else { - RewriteColor::ChangeAll(Color::WHITE.alpha(0.2)) - }, - RewriteColor::ChangeAll(colors::HOVERING), - ctx, - ) - .change_tooltip(tooltip), - ) - .margin(5) + let (normal, bounds) = GeomBatch::from_svg( + ctx, + "../data/system/assets/speed/triangle.svg", + if setting >= s { + RewriteColor::NoOp + } else { + RewriteColor::ChangeAll(Color::WHITE.alpha(0.2)) + }, + ); + let mut hovered = normal.clone(); + hovered.rewrite_color(RewriteColor::ChangeAll(colors::HOVERING)); + + Btn::custom(normal, hovered, bounds.get_rectangle()) + .tooltip(txt) + .build(ctx, label, None) + .margin(5) }) .collect(), )