more button refactor:

- custom tooltips
- get rid of a few old Button constructors
This commit is contained in:
Dustin Carlino 2020-03-17 09:46:51 -07:00
parent be9671a51f
commit 6de415033e
6 changed files with 284 additions and 271 deletions

View File

@ -389,6 +389,16 @@ impl GeomBatch {
}
}
pub fn from_svg<I: Into<String>>(
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,

View File

@ -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<MultiKey>,
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<MultiKey>,
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<I: Into<String>>(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<I: Into<String>>(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<I: Into<String>>(label: I) -> BtnBuilder {
BtnBuilder::TextBG1(label.into())
BtnBuilder::TextBG1(label.into(), None)
}
// The white background. WrappedComposite::text_bg_button.
pub fn text_bg2<I: Into<String>>(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<Text>),
TextFG(String, Option<Text>),
TextBG1(String, Option<Text>),
TextBG2(String, Option<Text>),
Custom(GeomBatch, GeomBatch, Polygon, Option<Text>),
}
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<I: Into<String>>(
self,
ctx: &EventCtx,
@ -300,48 +281,86 @@ impl BtnBuilder {
key: Option<MultiKey>,
) -> 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<MultiKey>) -> 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)
}

View File

@ -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()),

View File

@ -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![(

View File

@ -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 {

View File

@ -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(),
)