mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 16:36:02 +03:00
change settings live for traffic/delay graphs [rebuild]
This commit is contained in:
parent
6809330dda
commit
7be5b3f34a
@ -664,7 +664,7 @@ impl Composite {
|
|||||||
self.find::<TextBox>(name).get_line()
|
self.find::<TextBox>(name).get_line()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dropdown_value<T: 'static + PartialEq + Clone>(&mut self, name: &str) -> T {
|
pub fn dropdown_value<T: 'static + PartialEq + Clone>(&self, name: &str) -> T {
|
||||||
self.find::<Dropdown<T>>(name).current_value()
|
self.find::<Dropdown<T>>(name).current_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ impl<T: 'static + Ord + PartialEq + Copy + core::fmt::Debug + Yvalue<T>> Plot<T>
|
|||||||
let height = 0.2 * ctx.canvas.window_height;
|
let height = 0.2 * ctx.canvas.window_height;
|
||||||
|
|
||||||
let radius = 15.0;
|
let radius = 15.0;
|
||||||
let legend = Widget::col(
|
let legend = Widget::row(
|
||||||
series
|
series
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| {
|
.map(|s| {
|
||||||
@ -63,7 +63,8 @@ impl<T: 'static + Ord + PartialEq + Copy + core::fmt::Debug + Yvalue<T>> Plot<T>
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
)
|
||||||
|
.flex_wrap(ctx, 24);
|
||||||
|
|
||||||
// Assume min_x is Time::START_OF_DAY and min_y is y_zero
|
// Assume min_x is Time::START_OF_DAY and min_y is y_zero
|
||||||
let max_x = opts.max_x.unwrap_or_else(|| {
|
let max_x = opts.max_x.unwrap_or_else(|| {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::helpers::rotating_color_map;
|
use crate::helpers::rotating_color_map;
|
||||||
use crate::info::{header_btns, make_tabs, throughput, Details, Tab};
|
use crate::info::{header_btns, make_tabs, throughput, DataOptions, Details, Tab};
|
||||||
use abstutil::prettyprint_usize;
|
use abstutil::prettyprint_usize;
|
||||||
use ezgui::{EventCtx, Line, Plot, PlotOptions, Series, Text, TextExt, Widget};
|
use ezgui::{EventCtx, Line, Plot, PlotOptions, Series, Text, Widget};
|
||||||
use geom::{Duration, Statistic, Time};
|
use geom::{Duration, Statistic, Time};
|
||||||
use map_model::{IntersectionID, IntersectionType};
|
use map_model::{IntersectionID, IntersectionType};
|
||||||
use sim::Analytics;
|
use sim::Analytics;
|
||||||
@ -27,12 +27,19 @@ pub fn info(ctx: &EventCtx, app: &App, details: &mut Details, id: IntersectionID
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn traffic(
|
pub fn traffic(
|
||||||
ctx: &EventCtx,
|
ctx: &mut EventCtx,
|
||||||
app: &App,
|
app: &App,
|
||||||
details: &mut Details,
|
details: &mut Details,
|
||||||
id: IntersectionID,
|
id: IntersectionID,
|
||||||
|
opts: &DataOptions,
|
||||||
) -> Vec<Widget> {
|
) -> Vec<Widget> {
|
||||||
let mut rows = header(ctx, app, details, id, Tab::IntersectionTraffic(id));
|
let mut rows = header(
|
||||||
|
ctx,
|
||||||
|
app,
|
||||||
|
details,
|
||||||
|
id,
|
||||||
|
Tab::IntersectionTraffic(id, opts.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
let mut txt = Text::new();
|
let mut txt = Text::new();
|
||||||
|
|
||||||
@ -47,38 +54,54 @@ pub fn traffic(
|
|||||||
.get(id)
|
.get(id)
|
||||||
)
|
)
|
||||||
)));
|
)));
|
||||||
txt.add(Line(format!("In 20 minute buckets:")));
|
|
||||||
rows.push(txt.draw(ctx));
|
rows.push(txt.draw(ctx));
|
||||||
|
|
||||||
|
rows.push(opts.to_controls(ctx, app));
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
throughput(ctx, app, move |a, t| {
|
throughput(
|
||||||
a.throughput_intersection(t, id, Duration::minutes(20))
|
ctx,
|
||||||
})
|
app,
|
||||||
|
move |a, t| a.throughput_intersection(t, id, opts.bucket_size),
|
||||||
|
opts.show_baseline,
|
||||||
|
)
|
||||||
.margin(10),
|
.margin(10),
|
||||||
);
|
);
|
||||||
|
|
||||||
rows
|
rows
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delay(ctx: &EventCtx, app: &App, details: &mut Details, id: IntersectionID) -> Vec<Widget> {
|
pub fn delay(
|
||||||
let mut rows = header(ctx, app, details, id, Tab::IntersectionDelay(id));
|
ctx: &mut EventCtx,
|
||||||
|
app: &App,
|
||||||
|
details: &mut Details,
|
||||||
|
id: IntersectionID,
|
||||||
|
opts: &DataOptions,
|
||||||
|
) -> Vec<Widget> {
|
||||||
|
let mut rows = header(
|
||||||
|
ctx,
|
||||||
|
app,
|
||||||
|
details,
|
||||||
|
id,
|
||||||
|
Tab::IntersectionDelay(id, opts.clone()),
|
||||||
|
);
|
||||||
let i = app.primary.map.get_i(id);
|
let i = app.primary.map.get_i(id);
|
||||||
|
|
||||||
assert!(i.is_traffic_signal());
|
assert!(i.is_traffic_signal());
|
||||||
rows.push("In 20 minute buckets".draw_text(ctx));
|
rows.push(opts.to_controls(ctx, app));
|
||||||
|
|
||||||
rows.push(delay_plot(ctx, app, id, Duration::minutes(20)).margin(10));
|
rows.push(delay_plot(ctx, app, id, opts).margin(10));
|
||||||
|
|
||||||
rows
|
rows
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delay_plot(ctx: &EventCtx, app: &App, i: IntersectionID, bucket: Duration) -> Widget {
|
fn delay_plot(ctx: &EventCtx, app: &App, i: IntersectionID, opts: &DataOptions) -> Widget {
|
||||||
let get_data = |a: &Analytics, t: Time| {
|
let get_data = |a: &Analytics, t: Time| {
|
||||||
let mut series: Vec<(Statistic, Vec<(Time, Duration)>)> = Statistic::all()
|
let mut series: Vec<(Statistic, Vec<(Time, Duration)>)> = Statistic::all()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|stat| (stat, Vec::new()))
|
.map(|stat| (stat, Vec::new()))
|
||||||
.collect();
|
.collect();
|
||||||
for (t, distrib) in a.intersection_delays_bucketized(t, i, bucket) {
|
for (t, distrib) in a.intersection_delays_bucketized(t, i, opts.bucket_size) {
|
||||||
for (stat, pts) in series.iter_mut() {
|
for (stat, pts) in series.iter_mut() {
|
||||||
if distrib.count() == 0 {
|
if distrib.count() == 0 {
|
||||||
pts.push((t, Duration::ZERO));
|
pts.push((t, Duration::ZERO));
|
||||||
@ -101,7 +124,7 @@ fn delay_plot(ctx: &EventCtx, app: &App, i: IntersectionID, bucket: Duration) ->
|
|||||||
pts,
|
pts,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if app.has_prebaked().is_some() {
|
if opts.show_baseline {
|
||||||
for (idx, (stat, pts)) in get_data(app.prebaked(), Time::END_OF_DAY)
|
for (idx, (stat, pts)) in get_data(app.prebaked(), Time::END_OF_DAY)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -142,10 +165,13 @@ fn header(
|
|||||||
rows.push(make_tabs(ctx, &mut details.hyperlinks, tab, {
|
rows.push(make_tabs(ctx, &mut details.hyperlinks, tab, {
|
||||||
let mut tabs = vec![
|
let mut tabs = vec![
|
||||||
("Info", Tab::IntersectionInfo(id)),
|
("Info", Tab::IntersectionInfo(id)),
|
||||||
("Traffic", Tab::IntersectionTraffic(id)),
|
(
|
||||||
|
"Traffic",
|
||||||
|
Tab::IntersectionTraffic(id, DataOptions::new(app)),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
if i.is_traffic_signal() {
|
if i.is_traffic_signal() {
|
||||||
tabs.push(("Delay", Tab::IntersectionDelay(id)));
|
tabs.push(("Delay", Tab::IntersectionDelay(id, DataOptions::new(app))));
|
||||||
}
|
}
|
||||||
tabs
|
tabs
|
||||||
}));
|
}));
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::info::{header_btns, make_table, make_tabs, throughput, Details, Tab};
|
use crate::info::{header_btns, make_table, make_tabs, throughput, DataOptions, Details, Tab};
|
||||||
use abstutil::prettyprint_usize;
|
use abstutil::prettyprint_usize;
|
||||||
use ezgui::{EventCtx, Line, Text, TextExt, Widget};
|
use ezgui::{EventCtx, Line, Text, TextExt, Widget};
|
||||||
use geom::Duration;
|
|
||||||
use map_model::LaneID;
|
use map_model::LaneID;
|
||||||
|
|
||||||
pub fn info(ctx: &EventCtx, app: &App, details: &mut Details, id: LaneID) -> Vec<Widget> {
|
pub fn info(ctx: &EventCtx, app: &App, details: &mut Details, id: LaneID) -> Vec<Widget> {
|
||||||
@ -93,8 +92,14 @@ pub fn debug(ctx: &EventCtx, app: &App, details: &mut Details, id: LaneID) -> Ve
|
|||||||
rows
|
rows
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn traffic(ctx: &EventCtx, app: &App, details: &mut Details, id: LaneID) -> Vec<Widget> {
|
pub fn traffic(
|
||||||
let mut rows = header(ctx, app, details, id, Tab::LaneTraffic(id));
|
ctx: &mut EventCtx,
|
||||||
|
app: &App,
|
||||||
|
details: &mut Details,
|
||||||
|
id: LaneID,
|
||||||
|
opts: &DataOptions,
|
||||||
|
) -> Vec<Widget> {
|
||||||
|
let mut rows = header(ctx, app, details, id, Tab::LaneTraffic(id, opts.clone()));
|
||||||
let map = &app.primary.map;
|
let map = &app.primary.map;
|
||||||
let l = map.get_l(id);
|
let l = map.get_l(id);
|
||||||
let r = map.get_r(l.parent);
|
let r = map.get_r(l.parent);
|
||||||
@ -112,14 +117,18 @@ pub fn traffic(ctx: &EventCtx, app: &App, details: &mut Details, id: LaneID) ->
|
|||||||
.get(r.id)
|
.get(r.id)
|
||||||
)
|
)
|
||||||
)));
|
)));
|
||||||
txt.add(Line(format!("In 20 minute buckets:")));
|
|
||||||
rows.push(txt.draw(ctx));
|
rows.push(txt.draw(ctx));
|
||||||
|
|
||||||
|
rows.push(opts.to_controls(ctx, app));
|
||||||
|
|
||||||
let r = map.get_l(id).parent;
|
let r = map.get_l(id).parent;
|
||||||
rows.push(
|
rows.push(
|
||||||
throughput(ctx, app, move |a, t| {
|
throughput(
|
||||||
a.throughput_road(t, r, Duration::minutes(20))
|
ctx,
|
||||||
})
|
app,
|
||||||
|
move |a, t| a.throughput_road(t, r, opts.bucket_size),
|
||||||
|
opts.show_baseline,
|
||||||
|
)
|
||||||
.margin(10),
|
.margin(10),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -148,7 +157,7 @@ fn header(ctx: &EventCtx, app: &App, details: &mut Details, id: LaneID, tab: Tab
|
|||||||
tab,
|
tab,
|
||||||
vec![
|
vec![
|
||||||
("Info", Tab::LaneInfo(id)),
|
("Info", Tab::LaneInfo(id)),
|
||||||
("Traffic", Tab::LaneTraffic(id)),
|
("Traffic", Tab::LaneTraffic(id, DataOptions::new(app))),
|
||||||
("Debug", Tab::LaneDebug(id)),
|
("Debug", Tab::LaneDebug(id)),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
|
@ -13,10 +13,11 @@ use crate::game::Transition;
|
|||||||
use crate::helpers::ID;
|
use crate::helpers::ID;
|
||||||
use crate::render::{ExtraShapeID, MIN_ZOOM_FOR_DETAIL};
|
use crate::render::{ExtraShapeID, MIN_ZOOM_FOR_DETAIL};
|
||||||
use ezgui::{
|
use ezgui::{
|
||||||
hotkey, Btn, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
|
hotkey, Btn, Choice, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx,
|
||||||
Line, Outcome, Plot, PlotOptions, Series, Text, TextExt, VerticalAlignment, Widget,
|
HorizontalAlignment, Key, Line, Outcome, Plot, PlotOptions, Series, Text, TextExt,
|
||||||
|
VerticalAlignment, Widget,
|
||||||
};
|
};
|
||||||
use geom::{Circle, Distance, Time};
|
use geom::{Circle, Distance, Duration, Time};
|
||||||
use map_model::{AreaID, BuildingID, BusStopID, IntersectionID, LaneID};
|
use map_model::{AreaID, BuildingID, BusStopID, IntersectionID, LaneID};
|
||||||
use sim::{AgentID, Analytics, CarID, PedestrianID, PersonID, PersonState, TripMode, VehicleType};
|
use sim::{AgentID, Analytics, CarID, PedestrianID, PersonID, PersonState, TripMode, VehicleType};
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
@ -58,12 +59,12 @@ pub enum Tab {
|
|||||||
ExtraShape(ExtraShapeID),
|
ExtraShape(ExtraShapeID),
|
||||||
|
|
||||||
IntersectionInfo(IntersectionID),
|
IntersectionInfo(IntersectionID),
|
||||||
IntersectionTraffic(IntersectionID),
|
IntersectionTraffic(IntersectionID, DataOptions),
|
||||||
IntersectionDelay(IntersectionID),
|
IntersectionDelay(IntersectionID, DataOptions),
|
||||||
|
|
||||||
LaneInfo(LaneID),
|
LaneInfo(LaneID),
|
||||||
LaneDebug(LaneID),
|
LaneDebug(LaneID),
|
||||||
LaneTraffic(LaneID),
|
LaneTraffic(LaneID, DataOptions),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tab {
|
impl Tab {
|
||||||
@ -118,10 +119,27 @@ impl Tab {
|
|||||||
Tab::Crowd(members) => Some(ID::PedCrowd(members)),
|
Tab::Crowd(members) => Some(ID::PedCrowd(members)),
|
||||||
Tab::Area(a) => Some(ID::Area(a)),
|
Tab::Area(a) => Some(ID::Area(a)),
|
||||||
Tab::ExtraShape(es) => Some(ID::ExtraShape(es)),
|
Tab::ExtraShape(es) => Some(ID::ExtraShape(es)),
|
||||||
Tab::IntersectionInfo(i) | Tab::IntersectionTraffic(i) | Tab::IntersectionDelay(i) => {
|
Tab::IntersectionInfo(i)
|
||||||
Some(ID::Intersection(i))
|
| Tab::IntersectionTraffic(i, _)
|
||||||
|
| Tab::IntersectionDelay(i, _) => Some(ID::Intersection(i)),
|
||||||
|
Tab::LaneInfo(l) | Tab::LaneDebug(l) | Tab::LaneTraffic(l, _) => Some(ID::Lane(l)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn changed_settings(&self, c: &Composite) -> Option<Tab> {
|
||||||
|
let mut new_tab = self.clone();
|
||||||
|
match new_tab {
|
||||||
|
Tab::IntersectionTraffic(_, ref mut opts)
|
||||||
|
| Tab::IntersectionDelay(_, ref mut opts)
|
||||||
|
| Tab::LaneTraffic(_, ref mut opts) => {
|
||||||
|
*opts = DataOptions::from_controls(c);
|
||||||
}
|
}
|
||||||
Tab::LaneInfo(l) | Tab::LaneDebug(l) | Tab::LaneTraffic(l) => Some(ID::Lane(l)),
|
_ => {}
|
||||||
|
}
|
||||||
|
if &new_tab == self {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(new_tab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,13 +190,18 @@ impl InfoPanel {
|
|||||||
Tab::Area(a) => (debug::area(ctx, app, &mut details, a), true),
|
Tab::Area(a) => (debug::area(ctx, app, &mut details, a), true),
|
||||||
Tab::ExtraShape(es) => (debug::extra_shape(ctx, app, &mut details, es), true),
|
Tab::ExtraShape(es) => (debug::extra_shape(ctx, app, &mut details, es), true),
|
||||||
Tab::IntersectionInfo(i) => (intersection::info(ctx, app, &mut details, i), true),
|
Tab::IntersectionInfo(i) => (intersection::info(ctx, app, &mut details, i), true),
|
||||||
Tab::IntersectionTraffic(i) => {
|
Tab::IntersectionTraffic(i, ref opts) => (
|
||||||
(intersection::traffic(ctx, app, &mut details, i), false)
|
intersection::traffic(ctx, app, &mut details, i, opts),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
Tab::IntersectionDelay(i, ref opts) => {
|
||||||
|
(intersection::delay(ctx, app, &mut details, i, opts), false)
|
||||||
}
|
}
|
||||||
Tab::IntersectionDelay(i) => (intersection::delay(ctx, app, &mut details, i), false),
|
|
||||||
Tab::LaneInfo(l) => (lane::info(ctx, app, &mut details, l), true),
|
Tab::LaneInfo(l) => (lane::info(ctx, app, &mut details, l), true),
|
||||||
Tab::LaneDebug(l) => (lane::debug(ctx, app, &mut details, l), false),
|
Tab::LaneDebug(l) => (lane::debug(ctx, app, &mut details, l), false),
|
||||||
Tab::LaneTraffic(l) => (lane::traffic(ctx, app, &mut details, l), false),
|
Tab::LaneTraffic(l, ref opts) => {
|
||||||
|
(lane::traffic(ctx, app, &mut details, l, opts), false)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let maybe_id = tab.clone().to_id(app);
|
let maybe_id = tab.clone().to_id(app);
|
||||||
let mut cached_actions = Vec::new();
|
let mut cached_actions = Vec::new();
|
||||||
@ -358,7 +381,15 @@ impl InfoPanel {
|
|||||||
(close_panel, Some(t))
|
(close_panel, Some(t))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => (false, None),
|
None => {
|
||||||
|
// Maybe a non-click action should change the tab. Aka, checkboxes/dropdowns/etc on
|
||||||
|
// a tab.
|
||||||
|
if let Some(new_tab) = self.tab.changed_settings(&self.composite) {
|
||||||
|
*self = InfoPanel::new(ctx, app, new_tab, ctx_actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
(false, None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,6 +439,7 @@ fn throughput<F: Fn(&Analytics, Time) -> BTreeMap<TripMode, Vec<(Time, usize)>>>
|
|||||||
ctx: &EventCtx,
|
ctx: &EventCtx,
|
||||||
app: &App,
|
app: &App,
|
||||||
get_data: F,
|
get_data: F,
|
||||||
|
show_baseline: bool,
|
||||||
) -> Widget {
|
) -> Widget {
|
||||||
let mut series = get_data(app.primary.sim.get_analytics(), app.primary.sim.time())
|
let mut series = get_data(app.primary.sim.get_analytics(), app.primary.sim.time())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -417,7 +449,7 @@ fn throughput<F: Fn(&Analytics, Time) -> BTreeMap<TripMode, Vec<(Time, usize)>>>
|
|||||||
pts,
|
pts,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if app.has_prebaked().is_some() {
|
if show_baseline {
|
||||||
// TODO Ahh these colors don't show up differently at all.
|
// TODO Ahh these colors don't show up differently at all.
|
||||||
for (m, pts) in get_data(app.prebaked(), Time::END_OF_DAY) {
|
for (m, pts) in get_data(app.prebaked(), Time::END_OF_DAY) {
|
||||||
series.push(Series {
|
series.push(Series {
|
||||||
@ -482,3 +514,51 @@ pub trait ContextualActions {
|
|||||||
close_panel: &mut bool,
|
close_panel: &mut bool,
|
||||||
) -> Transition;
|
) -> Transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct DataOptions {
|
||||||
|
pub show_baseline: bool,
|
||||||
|
pub bucket_size: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataOptions {
|
||||||
|
pub fn new(app: &App) -> DataOptions {
|
||||||
|
DataOptions {
|
||||||
|
show_baseline: app.has_prebaked().is_some(),
|
||||||
|
bucket_size: Duration::minutes(20),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_controls(&self, ctx: &mut EventCtx, app: &App) -> Widget {
|
||||||
|
Widget::col(vec![
|
||||||
|
Widget::row(vec![
|
||||||
|
"In".draw_text(ctx),
|
||||||
|
Widget::dropdown(
|
||||||
|
ctx,
|
||||||
|
"bucket size",
|
||||||
|
self.bucket_size,
|
||||||
|
vec![
|
||||||
|
Choice::new("20 minute", Duration::minutes(20)),
|
||||||
|
Choice::new("1 hour", Duration::hours(1)),
|
||||||
|
Choice::new("6 hour", Duration::hours(6)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.margin(3),
|
||||||
|
"buckets".draw_text(ctx),
|
||||||
|
]),
|
||||||
|
if app.has_prebaked().is_some() {
|
||||||
|
// TODO Change the wording of this
|
||||||
|
Widget::checkbox(ctx, "Show baseline data", None, self.show_baseline)
|
||||||
|
} else {
|
||||||
|
Widget::nothing()
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_controls(c: &Composite) -> DataOptions {
|
||||||
|
DataOptions {
|
||||||
|
show_baseline: c.has_widget("Show baseline data") && c.is_checked("Show baseline data"),
|
||||||
|
bucket_size: c.dropdown_value("bucket size"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -149,10 +149,11 @@ impl std::fmt::Display for Duration {
|
|||||||
write!(f, "{}m", minutes)?;
|
write!(f, "{}m", minutes)?;
|
||||||
}
|
}
|
||||||
if remainder != 0 {
|
if remainder != 0 {
|
||||||
write!(f, "{}.{:01}s", seconds, remainder)
|
write!(f, "{}.{:01}s", seconds, remainder)?;
|
||||||
} else {
|
} else if seconds != 0 {
|
||||||
write!(f, "{}s", seconds)
|
write!(f, "{}s", seconds)?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user