mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
fix outline for spinner (without crashing!) and cache drawable
This commit is contained in:
parent
088e434a0a
commit
87a1a3f027
@ -194,7 +194,7 @@ fn params_to_controls(ctx: &mut EventCtx, mode: TripMode, params: &RoutingParams
|
||||
if mode == TripMode::Drive || mode == TripMode::Bike {
|
||||
rows.push(Widget::row(vec![
|
||||
"Unprotected turn penalty:".draw_text(ctx).margin_right(20),
|
||||
Spinner::new(
|
||||
Spinner::widget(
|
||||
ctx,
|
||||
(1, 100),
|
||||
(params.unprotected_turn_penalty * 10.0) as isize,
|
||||
@ -206,17 +206,17 @@ fn params_to_controls(ctx: &mut EventCtx, mode: TripMode, params: &RoutingParams
|
||||
// TODO Spinners that natively understand a floating point range with a given precision
|
||||
rows.push(Widget::row(vec![
|
||||
"Bike lane penalty:".draw_text(ctx).margin_right(20),
|
||||
Spinner::new(ctx, (0, 20), (params.bike_lane_penalty * 10.0) as isize)
|
||||
Spinner::widget(ctx, (0, 20), (params.bike_lane_penalty * 10.0) as isize)
|
||||
.named("bike lane penalty"),
|
||||
]));
|
||||
rows.push(Widget::row(vec![
|
||||
"Bus lane penalty:".draw_text(ctx).margin_right(20),
|
||||
Spinner::new(ctx, (0, 20), (params.bus_lane_penalty * 10.0) as isize)
|
||||
Spinner::widget(ctx, (0, 20), (params.bus_lane_penalty * 10.0) as isize)
|
||||
.named("bus lane penalty"),
|
||||
]));
|
||||
rows.push(Widget::row(vec![
|
||||
"Driving lane penalty:".draw_text(ctx).margin_right(20),
|
||||
Spinner::new(ctx, (0, 20), (params.driving_lane_penalty * 10.0) as isize)
|
||||
Spinner::widget(ctx, (0, 20), (params.driving_lane_penalty * 10.0) as isize)
|
||||
.named("driving lane penalty"),
|
||||
]));
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ impl RouteEditor {
|
||||
// TODO This UI needs design, just something to start plumbing the edits
|
||||
Widget::row(vec![
|
||||
"Frequency in minutes".draw_text(ctx),
|
||||
Spinner::new(ctx, (1, 120), 60).named("freq_mins"),
|
||||
Spinner::widget(ctx, (1, 120), 60).named("freq_mins"),
|
||||
]),
|
||||
ctx.style()
|
||||
.btn_solid_primary_text("Apply")
|
||||
|
@ -32,7 +32,7 @@ impl ChangeDuration {
|
||||
]),
|
||||
Widget::row(vec![
|
||||
"Seconds:".draw_text(ctx).centered_vert(),
|
||||
Spinner::new(
|
||||
Spinner::widget(
|
||||
ctx,
|
||||
(
|
||||
signal.get_min_crossing_time(idx).inner_seconds() as isize,
|
||||
@ -64,7 +64,7 @@ impl ChangeDuration {
|
||||
.draw(ctx)]),
|
||||
Widget::row(vec![
|
||||
"Seconds:".draw_text(ctx).centered_vert(),
|
||||
Spinner::new(
|
||||
Spinner::widget(
|
||||
ctx,
|
||||
(1, 300),
|
||||
match signal.stages[idx].stage_type {
|
||||
@ -81,7 +81,7 @@ impl ChangeDuration {
|
||||
.draw(ctx)]),
|
||||
Widget::row(vec![
|
||||
"Seconds:".draw_text(ctx).centered_vert(),
|
||||
Spinner::new(
|
||||
Spinner::widget(
|
||||
ctx,
|
||||
(1, 300),
|
||||
match signal.stages[idx].stage_type {
|
||||
|
@ -249,7 +249,7 @@ impl TuneRelative {
|
||||
.draw(ctx),
|
||||
Widget::row(vec![
|
||||
"Offset (seconds):".draw_text(ctx),
|
||||
Spinner::new(ctx, (0, 90), (offset2 - offset1).inner_seconds() as isize)
|
||||
Spinner::widget(ctx, (0, 90), (offset2 - offset1).inner_seconds() as isize)
|
||||
.named("offset"),
|
||||
]),
|
||||
ctx.style()
|
||||
|
@ -59,7 +59,7 @@ impl ZoneEditor {
|
||||
Widget::row(vec![
|
||||
"Limit the number of vehicles passing through per hour (0 = unlimited):"
|
||||
.draw_text(ctx),
|
||||
Spinner::new(ctx, (0, 1000), cap_vehicles_per_hour.unwrap_or(0) as isize)
|
||||
Spinner::widget(ctx, (0, 1000), cap_vehicles_per_hour.unwrap_or(0) as isize)
|
||||
.named("cap_vehicles"),
|
||||
]),
|
||||
Widget::custom_row(vec![
|
||||
|
@ -56,7 +56,7 @@ impl TrafficSignalDemand {
|
||||
.draw(ctx),
|
||||
Widget::row(vec![
|
||||
"Hour:".draw_text(ctx),
|
||||
Spinner::new(ctx, (0, 24), 7).named("hour"),
|
||||
Spinner::widget(ctx, (0, 24), 7).named("hour"),
|
||||
]),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)
|
||||
@ -111,11 +111,11 @@ impl State<App> for TrafficSignalDemand {
|
||||
_ => {}
|
||||
}
|
||||
if ctx.input.pressed(Key::LeftArrow) {
|
||||
self.panel.modify_spinner("hour", -1);
|
||||
self.panel.modify_spinner(ctx, "hour", -1);
|
||||
changed = true;
|
||||
}
|
||||
if ctx.input.pressed(Key::RightArrow) {
|
||||
self.panel.modify_spinner("hour", 1);
|
||||
self.panel.modify_spinner(ctx, "hour", 1);
|
||||
changed = true;
|
||||
}
|
||||
if changed {
|
||||
|
@ -320,7 +320,7 @@ impl AgentSpawner {
|
||||
]),
|
||||
Widget::row(vec![
|
||||
"Number of trips:".draw_text(ctx),
|
||||
Spinner::new(ctx, (1, 1000), 1).named("number"),
|
||||
Spinner::widget(ctx, (1, 1000), 1).named("number"),
|
||||
]),
|
||||
ctx.style()
|
||||
.btn_solid_primary_text("Confirm")
|
||||
|
@ -218,7 +218,7 @@ impl EditScenarioModifiers {
|
||||
.build_def(ctx),
|
||||
);
|
||||
rows.push(Widget::row(vec![
|
||||
Spinner::new(ctx, (2, 14), 2).named("repeat_days"),
|
||||
Spinner::widget(ctx, (2, 14), 2).named("repeat_days"),
|
||||
ctx.style()
|
||||
.btn_outline_text("Repeat schedule multiple days")
|
||||
.build_def(ctx),
|
||||
@ -364,7 +364,7 @@ impl ChangeMode {
|
||||
"Percent of people to modify:"
|
||||
.draw_text(ctx)
|
||||
.centered_vert(),
|
||||
Spinner::new(ctx, (1, 100), 50).named("pct_ppl"),
|
||||
Spinner::widget(ctx, (1, 100), 50).named("pct_ppl"),
|
||||
]),
|
||||
"Types of trips to convert:".draw_text(ctx),
|
||||
checkbox_per_mode(ctx, app, &btreeset! { TripMode::Drive }),
|
||||
|
@ -34,7 +34,7 @@ impl EditRoad {
|
||||
let controls = Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
"lanes:forward".draw_text(ctx).margin_right(20),
|
||||
Spinner::new(
|
||||
Spinner::widget(
|
||||
ctx,
|
||||
(1, 5),
|
||||
road.osm_tags
|
||||
@ -46,7 +46,7 @@ impl EditRoad {
|
||||
]),
|
||||
Widget::row(vec![
|
||||
"lanes:backward".draw_text(ctx).margin_right(20),
|
||||
Spinner::new(
|
||||
Spinner::widget(
|
||||
ctx,
|
||||
(0, 5),
|
||||
road.osm_tags
|
||||
|
@ -160,7 +160,7 @@ impl OptionsPanel {
|
||||
),
|
||||
Widget::row(vec![
|
||||
"Scroll speed for menus".draw_text(ctx).centered_vert(),
|
||||
Spinner::new(ctx, (1, 50), ctx.canvas.gui_scroll_speed as isize)
|
||||
Spinner::widget(ctx, (1, 50), ctx.canvas.gui_scroll_speed as isize)
|
||||
.named("gui_scroll_speed"),
|
||||
]),
|
||||
])
|
||||
|
@ -41,7 +41,7 @@ impl HeatmapOptions {
|
||||
// TODO Display the value...
|
||||
Widget::row(vec![
|
||||
"Resolution (meters)".draw_text(ctx).centered_vert(),
|
||||
Spinner::new(ctx, (1, 100), self.resolution as isize)
|
||||
Spinner::widget(ctx, (1, 100), self.resolution as isize)
|
||||
.named("resolution")
|
||||
.align_right(),
|
||||
]),
|
||||
@ -49,7 +49,7 @@ impl HeatmapOptions {
|
||||
"Radius (resolution multiplier)"
|
||||
.draw_text(ctx)
|
||||
.centered_vert(),
|
||||
Spinner::new(ctx, (0, 10), self.radius as isize)
|
||||
Spinner::widget(ctx, (0, 10), self.radius as isize)
|
||||
.named("radius")
|
||||
.align_right(),
|
||||
]),
|
||||
|
@ -144,7 +144,11 @@ impl<A: AppLike + 'static, T: MinimapControls<A>> Minimap<A, T> {
|
||||
if self.controls.has_zorder(app) {
|
||||
Widget::col(vec![
|
||||
Line("Z-order:").small().draw(ctx),
|
||||
Spinner::new(ctx, app.draw_map().zorder_range, app.draw_map().show_zorder)
|
||||
Spinner::widget(
|
||||
ctx,
|
||||
app.draw_map().zorder_range,
|
||||
app.draw_map().show_zorder,
|
||||
)
|
||||
.named("zorder"),
|
||||
])
|
||||
.margin_above(10)
|
||||
|
@ -377,8 +377,8 @@ impl Panel {
|
||||
pub fn spinner(&self, name: &str) -> isize {
|
||||
self.find::<Spinner>(name).current
|
||||
}
|
||||
pub fn modify_spinner(&mut self, name: &str, delta: isize) {
|
||||
self.find_mut::<Spinner>(name).modify(delta)
|
||||
pub fn modify_spinner(&mut self, ctx: &EventCtx, name: &str, delta: isize) {
|
||||
self.find_mut::<Spinner>(name).modify(ctx, delta)
|
||||
}
|
||||
|
||||
pub fn dropdown_value<T: 'static + PartialEq + Clone, I: Into<String>>(&self, name: I) -> T {
|
||||
|
@ -1,8 +1,9 @@
|
||||
use geom::{Polygon, Pt2D};
|
||||
use geom::{CornerRadii, Distance, Polygon, Pt2D};
|
||||
|
||||
use crate::{
|
||||
include_labeled_bytes, text, Button, EdgeInsets, EventCtx, GeomBatch, GfxCtx, Line, Outcome,
|
||||
ScreenDims, ScreenPt, ScreenRectangle, StyledButtons, Text, Widget, WidgetImpl, WidgetOutput,
|
||||
include_labeled_bytes, text, Button, Drawable, EdgeInsets, EventCtx, GeomBatch, GfxCtx, Line,
|
||||
Outcome, OutlineStyle, Prerender, ScreenDims, ScreenPt, ScreenRectangle, Style, StyledButtons,
|
||||
Text, Widget, WidgetImpl, WidgetOutput,
|
||||
};
|
||||
|
||||
// TODO MAX_CHAR_WIDTH is a hardcoded nonsense value
|
||||
@ -18,13 +19,19 @@ pub struct Spinner {
|
||||
|
||||
up: Button,
|
||||
down: Button,
|
||||
outline: OutlineStyle,
|
||||
drawable: Drawable,
|
||||
|
||||
top_left: ScreenPt,
|
||||
dims: ScreenDims,
|
||||
}
|
||||
|
||||
impl Spinner {
|
||||
pub fn new(ctx: &EventCtx, (low, high): (isize, isize), mut current: isize) -> Widget {
|
||||
pub fn widget(ctx: &EventCtx, (low, high): (isize, isize), current: isize) -> Widget {
|
||||
Widget::new(Box::new(Self::new(ctx, (low, high), current)))
|
||||
}
|
||||
|
||||
pub fn new(ctx: &EventCtx, (low, high): (isize, isize), mut current: isize) -> Self {
|
||||
let button_builder = ctx
|
||||
.style()
|
||||
.btn_plain()
|
||||
@ -39,12 +46,25 @@ impl Spinner {
|
||||
let up = button_builder
|
||||
.clone()
|
||||
.image_bytes(include_labeled_bytes!("../../icons/arrow_up.svg"))
|
||||
.corner_rounding(CornerRadii {
|
||||
top_left: 0.0,
|
||||
top_right: 5.0,
|
||||
bottom_right: 0.0,
|
||||
bottom_left: 5.0,
|
||||
})
|
||||
.build(ctx, "increase value");
|
||||
|
||||
let down = button_builder
|
||||
.image_bytes(include_labeled_bytes!("../../icons/arrow_down.svg"))
|
||||
.corner_rounding(CornerRadii {
|
||||
top_left: 5.0,
|
||||
top_right: 0.0,
|
||||
bottom_right: 5.0,
|
||||
bottom_left: 0.0,
|
||||
})
|
||||
.build(ctx, "decrease value");
|
||||
|
||||
let outline = ctx.style().btn_outline.outline;
|
||||
let dims = ScreenDims::new(
|
||||
TEXT_WIDTH + up.get_dims().width,
|
||||
up.get_dims().height + down.get_dims().height + 1.0,
|
||||
@ -57,25 +77,46 @@ impl Spinner {
|
||||
warn!("Spinner current value is out of bounds!");
|
||||
}
|
||||
|
||||
let outline = ctx.style().btn_outline.outline;
|
||||
Widget::new(Box::new(Spinner {
|
||||
let mut spinner = Spinner {
|
||||
low,
|
||||
high,
|
||||
current,
|
||||
|
||||
up,
|
||||
down,
|
||||
|
||||
drawable: Drawable::empty(ctx),
|
||||
outline,
|
||||
top_left: ScreenPt::new(0.0, 0.0),
|
||||
dims,
|
||||
}))
|
||||
.outline(outline)
|
||||
};
|
||||
spinner.drawable = spinner.drawable(ctx.prerender, ctx.style());
|
||||
spinner
|
||||
}
|
||||
|
||||
pub fn modify(&mut self, delta: isize) {
|
||||
pub fn modify(&mut self, ctx: &EventCtx, delta: isize) {
|
||||
self.current += delta;
|
||||
self.current = self.current.min(self.high);
|
||||
self.current = self.current.max(self.low);
|
||||
self.drawable = self.drawable(ctx.prerender, ctx.style());
|
||||
}
|
||||
|
||||
fn drawable(&self, prerender: &Prerender, style: &Style) -> Drawable {
|
||||
let mut batch = GeomBatch::from(vec![(
|
||||
style.field_bg,
|
||||
Polygon::rounded_rectangle(self.dims.width, self.dims.height, 5.0),
|
||||
)]);
|
||||
batch.append(
|
||||
Text::from(Line(self.current.to_string()))
|
||||
.render_autocropped(prerender)
|
||||
.centered_on(Pt2D::new(TEXT_WIDTH / 2.0, self.dims.height / 2.0)),
|
||||
);
|
||||
batch.push(
|
||||
self.outline.1,
|
||||
Polygon::rounded_rectangle(self.dims.width, self.dims.height, 5.0)
|
||||
.to_outline(Distance::meters(self.outline.0))
|
||||
.unwrap(),
|
||||
);
|
||||
prerender.upload(batch)
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +142,7 @@ impl WidgetImpl for Spinner {
|
||||
if let Outcome::Clicked(_) = output.outcome {
|
||||
output.outcome = Outcome::Changed;
|
||||
self.current = (self.current + 1).min(self.high);
|
||||
self.drawable = self.drawable(&ctx.prerender, ctx.style());
|
||||
ctx.no_op_event(true, |ctx| self.up.event(ctx, output));
|
||||
return;
|
||||
}
|
||||
@ -109,6 +151,7 @@ impl WidgetImpl for Spinner {
|
||||
if let Outcome::Clicked(_) = output.outcome {
|
||||
output.outcome = Outcome::Changed;
|
||||
self.current = (self.current - 1).max(self.low);
|
||||
self.drawable = self.drawable(&ctx.prerender, ctx.style());
|
||||
ctx.no_op_event(true, |ctx| self.down.event(ctx, output));
|
||||
return;
|
||||
}
|
||||
@ -119,10 +162,12 @@ impl WidgetImpl for Spinner {
|
||||
if dy > 0.0 && self.current != self.high {
|
||||
self.current += 1;
|
||||
output.outcome = Outcome::Changed;
|
||||
self.drawable = self.drawable(&ctx.prerender, ctx.style());
|
||||
}
|
||||
if dy < 0.0 && self.current != self.low {
|
||||
self.current -= 1;
|
||||
output.outcome = Outcome::Changed;
|
||||
self.drawable = self.drawable(&ctx.prerender, ctx.style());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,18 +175,7 @@ impl WidgetImpl for Spinner {
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx) {
|
||||
// TODO Cache
|
||||
let mut batch = GeomBatch::from(vec![(
|
||||
g.style().panel_bg,
|
||||
Polygon::rounded_rectangle(self.dims.width, self.dims.height, 5.0),
|
||||
)]);
|
||||
batch.append(
|
||||
Text::from(Line(self.current.to_string()))
|
||||
.render_autocropped(g)
|
||||
.centered_on(Pt2D::new(TEXT_WIDTH / 2.0, self.dims.height / 2.0)),
|
||||
);
|
||||
let draw = g.upload(batch);
|
||||
g.redraw_at(self.top_left, &draw);
|
||||
g.redraw_at(self.top_left, &self.drawable);
|
||||
|
||||
self.up.draw(g);
|
||||
self.down.draw(g);
|
||||
|
@ -344,7 +344,7 @@ fn make_controls(ctx: &mut EventCtx) -> Panel {
|
||||
.build_widget(ctx, "btn_outline_icon_text"),
|
||||
])]),
|
||||
Text::from(Line("Spinner").big_heading_styled().size(18)).draw(ctx),
|
||||
widgetry::Spinner::new(ctx, (0, 11), 1),
|
||||
widgetry::Spinner::widget(ctx, (0, 11), 1),
|
||||
Widget::row(vec![
|
||||
ctx.style()
|
||||
.btn_outline_text("New faces")
|
||||
@ -474,7 +474,7 @@ fn make_controls(ctx: &mut EventCtx) -> Panel {
|
||||
),
|
||||
Widget::col(
|
||||
(0..row_height)
|
||||
.map(|_| widgetry::Spinner::new(ctx, (0, 11), 1))
|
||||
.map(|_| widgetry::Spinner::widget(ctx, (0, 11), 1))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
Widget::col(
|
||||
|
Loading…
Reference in New Issue
Block a user