New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
new tabs part 1 (widgetry demo, jump-to-time, and trips table) #571
Conversation
TODO: - tooltips (or is that just a responsibility of the button builder?) - widgetry color scheme should use day-theme - pick nice tab colors - start applying! - jump in time - info panel - "more data" - fix width to match widest? Or maybe width modes?
The GenericTable machinery lost a lot of it's utility now that we have proper tabs. I've left a couple of free functions to be shared, and inlined the remaining simple functionality.
Currently the color was only via transparency - so depending on what was behind the panel, there was sometimes not enough contrast.
} | ||
|
||
fn draw(&self, g: &mut GfxCtx, app: &App) { | ||
g.clear(app.cs.dialog_bg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I also got rid of the green backgrounds, because it felt more cohesive. Is there a reason we should keep it?
Relatedly, I don't really understand what draw_baselayer
is about, but once I removed the g.clear
here, I observed some glitchy rendering until I also removed DrawBaselayer::Custom
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also got rid of the green backgrounds, because it felt more cohesive
Agreed, still seeing the map underneath is nicer. The "fullscreen" design with the green background was just meant to draw focus away from the map and towards the data, I think, but the new design is just as easy to read, and more cohesive as you say.
I don't really understand what draw_baselayer is about
widgetry manages a stack of states. Most of the time, the current state is totally responsible for drawing everything, including initially clearing the screen of whatever was previously drawn there. Sometimes, we want to ask the previous state to draw, then draw on top of that. Most of the time, we want to do something default (SharedAppState::draw_default
) -- in this case, draw the map. Occasionally though, we want to avoid the (negligible) perf hit of rendering the map at all, so we use Custom
, which means we're responsible for doing g.clear
too.
So what you've done is go back to DefaultDraw
, which just renders the map, then draws on top of it. Totally fine!
DashTab::ParkingOverhead.picker(ctx, app), | ||
Widget::col(vec![ | ||
Widget::row(vec![ | ||
Text::from_multiline(vec![ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No changes to the content, other than that's it's now outlined and has "section" styling.
Widget::col(vec![ | ||
summary_boxes(ctx, app, &filter), | ||
Widget::row(vec![ | ||
contingency_table(ctx, app, &filter), | ||
scatter_plot(ctx, app, &filter), | ||
contingency_table(ctx, app, &filter).bg(ctx.style().section_bg), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to the tabs, but while I was here, I noticed a few things that should probably have "section" styling.
.image_color(solid_style.fg, ControlState::Default) | ||
.image_bg_color(solid_style.bg, ControlState::Default) | ||
.image_bg_color(solid_style.bg_hover, ControlState::Hovered) | ||
.image_color(self.btn_solid.fg, ControlState::Default) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
previously we used self.btn_tab
to style things like the icon portion of the "pop up" buttons (e.g. "switch scenario"), but the new tab styling isn't compatible with that usage, so I've introduced another button_style: btn_solid.
widgetry/src/widgets/panel.rs
Outdated
@@ -437,6 +437,12 @@ impl Panel { | |||
} | |||
} | |||
|
|||
pub fn swap_contained_content(&mut self, ctx: &EventCtx, name: &str, widget: &mut Widget) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about this name... maybe swap_inner_content
would be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused about what this is doing. Why is the first member of the row or column special and worth swapping out?
EDIT: Ah, it's because we're assuming name
was made from a .container()
call and ONLY has one item. Is it worth asserting that there's only one item in there below?
API-wise: maybe lock it down to pub(crate)
unless you have use cases outside of TabController
in mind, and add a bit of docstring?
The info panel behomoth, and a few other places we do tab-ish things (challenge picker, scenario picker) are still TODO, but I wanted to have an opportunity to check in on direction before going further - though these changes are intended to be mergeable as is. |
I agree it looks pretty bad, but to my eye, not worse than the current look: Can you clarify what change you'd like to see here? Is it that you want the buttons to continue to have a visible border/background rather than appearing as "plain"? |
.build_def(ctx), | ||
); | ||
hyperlinks.insert(name.to_string(), link); | ||
} | ||
{ | ||
// stop-gap color that is semi-legible across themes until the tab redesign is completed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though I haven't put the info-tabs into the new tab system, design wise, it's already pretty close. The only visual difference would be adding the "section" background to the tab's content, instead of a translucent background.
That might help a little bit, but probably not much. Do you find the contrast in the "more data" page tabs to be sufficiently legible?
This contrast issue is why I offset the panel_bg in ecca637
I could increase that effect to increase contrast, but it's such a key part of the overall design that I'm trying to diverge only as much as necessary to get readable content.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you find the contrast in the "more data" page tabs to be sufficiently legible?
Yes, but possibly only because the tabs happen to be on top of a section with the background, so the current tab kind of merges in:
It still feels hard to distinguish for me, but I'm fine with sticking close to the design for now. Thanks!
} | ||
} | ||
|
||
impl<T: 'static, F: 'static, P: 'static + Fn(&mut EventCtx, &App, &Table<App, T, F>) -> Panel> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am thrilled to see this gutted in favor of something more generic. Wooot!
} | ||
|
||
fn draw(&self, g: &mut GfxCtx, app: &App) { | ||
g.clear(app.cs.dialog_bg); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also got rid of the green backgrounds, because it felt more cohesive
Agreed, still seeing the map underneath is nicer. The "fullscreen" design with the green background was just meant to draw focus away from the map and towards the data, I think, but the new design is just as easy to read, and more cohesive as you say.
I don't really understand what draw_baselayer is about
widgetry manages a stack of states. Most of the time, the current state is totally responsible for drawing everything, including initially clearing the screen of whatever was previously drawn there. Sometimes, we want to ask the previous state to draw, then draw on top of that. Most of the time, we want to do something default (SharedAppState::draw_default
) -- in this case, draw the map. Occasionally though, we want to avoid the (negligible) perf hit of rendering the map at all, so we use Custom
, which means we're responsible for doing g.clear
too.
So what you've done is go back to DefaultDraw
, which just renders the map, then draws on top of it. Totally fine!
@@ -18,9 +18,7 @@ mod trip_table; | |||
// Oh the dashboards melted, but we still had the radio |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You were so close to my Modest Mouse reference, but no comment about it? :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All post-2000 MM references are lost on me, but I'll check it out as penance.
use widgetry::{EventCtx, Filler, Line, Panel, State, Text, Toggle, Widget}; | ||
use widgetry::{EventCtx, Filler, GfxCtx, Line, Outcome, Panel, State, Text, Toggle, Widget}; | ||
|
||
type Transition = widgetry::Transition<App>; | ||
|
||
use crate::app::App; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can pull in crate::app::Transition
here instead of the typedef above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
} else if self.table_tabs.handle_action(ctx, &x, &mut self.panel) { | ||
// if true, tabs handled the action | ||
} else { | ||
todo!("unhandled action: {}", x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: panic
instead of todo
, since this should logically be unreachable in its current form, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition { | ||
match self.panel.event(ctx) { | ||
Outcome::Clicked(x) => { | ||
if self.table_tabs.active_tab_idx() == 0 && self.finished_trips_table.clicked(&x) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like the improvements to the code here! Using push_tab
to build them up and keeping all of the tabs "alive" at all times is quite intuitive. There's a little bit of boilerplate to associate the tab index with the content and route events, but I think it's very localized and easy to understand. The alternatives are likely much more bizarre to express in Rust, with callbacks or implementing some complicated trait. So I like this!
Looking forward to applying this to info panels in particular, there might be some more complications with conceptually keeping all tabs' content "active" at the same time. Some tabs auto-update as time passes, and this can be a bit expensive, so we wouldn't want to necessarily do it for multiple tabs. Also they fill out this weird Details
struct to draw extra zoomed/unzoomed stuff on the map, and it wouldn't make sense to draw multiple tabs' extra content at once. Maybe we just wind up using the new tab button style there, but for the moment, leave the existing weird structures in place for updating/switching tabs.
game/src/sandbox/time_warp.rs
Outdated
if self.tabs.handle_action(ctx, action, &mut self.panel) { | ||
// if true, tabs has handled the action | ||
} else { | ||
todo!("handle action: {}", action) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing this was an intermediate step of developing the tab controller. Should we change this to an assert
or something else now that this case should be unreachable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done!
widgetry/src/widgets/panel.rs
Outdated
@@ -437,6 +437,12 @@ impl Panel { | |||
} | |||
} | |||
|
|||
pub fn swap_contained_content(&mut self, ctx: &EventCtx, name: &str, widget: &mut Widget) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused about what this is doing. Why is the first member of the row or column special and worth swapping out?
EDIT: Ah, it's because we're assuming name
was made from a .container()
call and ONLY has one item. Is it worth asserting that there's only one item in there below?
API-wise: maybe lock it down to pub(crate)
unless you have use cases outside of TabController
in mind, and add a bit of docstring?
@@ -136,6 +144,9 @@ impl<A, T, F> Table<A, T, F> { | |||
make_table(ctx, headers, rows, 0.88 * ctx.canvas.window_width), | |||
make_pagination(ctx, num_filtered, self.skip), | |||
]) | |||
.named(&self.id) | |||
// return in separate container incase caller want to apply an outter-name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
incase -> in case
outter -> outer
The current look definitely is jarring. But yes, just looking like plain text makes it less clear it's a button. Wouldn't a slight outline or background work? |
I've renamed this method and its params and added a docstring. |
LGTM! Thanks for the PR. Let me know if you want to talk over how to apply this to info panels. |
We have several "tab" like systems in the app.
This PR works on consolidate those implementations, and incorporates some of the new designs.
As a reminder, here's the tab spec from figma:
The active tab has dark text, while the inactive tabs which you can switch to have faded text.
I chose to diverge from figma a bit, primarily because the "inactive" tabs were tough to read with the low-contrast text styling, especially on the translucent background in a way that wasn't obvious from figma.
before
The "finished", "canceled", "unfinished" tabs:
The time warp:
after
increasing contrast
One other thing I did, which might be controversial...
Previously the panel_bg and the panel_section were the same colors, but the panel_bg had some opacity applied. This often looked good, but if the map behind the panel has a bunch of content which is almost panel colored, then the contrast between panel_bg and panel_section is lost.
In particular, this could make the tabs hard to differentiate.
To address this, I doped the panel_bg to be a bit farther away from the section color. Compare the tab outline in the left image (doped) vs. right (pure).