style tabs

This commit is contained in:
Michael Kirk 2021-03-18 09:19:01 -07:00 committed by Dustin Carlino
parent be6be741f3
commit 7f00154ae6
14 changed files with 174 additions and 158 deletions

View File

@ -251,7 +251,7 @@ fn make_panel(
.fill_height()
.padding(42)
.bg(Color::WHITE)
.outline(ctx.style().btn_tab.outline),
.outline(ctx.style().btn_solid.outline),
];
Panel::new(Widget::custom_col(col))

View File

@ -149,7 +149,7 @@ impl ChallengesPicker {
.centered_horiz()
.bg(app.cs.panel_bg)
.padding(16)
.outline(ctx.style().btn_tab.outline),
.outline(ctx.style().btn_solid.outline),
];
// First list challenges
@ -174,7 +174,7 @@ impl ChallengesPicker {
.flex_wrap(ctx, Percent::int(80))
.bg(app.cs.panel_bg)
.padding(16)
.outline(ctx.style().btn_tab.outline),
.outline(ctx.style().btn_solid.outline),
);
let mut main_row = Vec::new();
@ -201,7 +201,7 @@ impl ChallengesPicker {
Widget::col(col)
.bg(app.cs.panel_bg)
.padding(16)
.outline(ctx.style().btn_tab.outline),
.outline(ctx.style().btn_solid.outline),
);
}
@ -243,7 +243,7 @@ impl ChallengesPicker {
Widget::col(inner_col)
.bg(app.cs.panel_bg)
.padding(16)
.outline(ctx.style().btn_tab.outline),
.outline(ctx.style().btn_solid.outline),
);
current_challenge = Some(challenge);
}

View File

@ -11,8 +11,8 @@ use sim::{
VehicleType,
};
use widgetry::{
Color, ControlState, Drawable, EventCtx, GeomBatch, GfxCtx, Key, Line, LinePlot, Outcome,
Panel, PlotOptions, Series, TextExt, Toggle, Widget,
Drawable, EventCtx, GeomBatch, GfxCtx, Key, Line, LinePlot, Outcome, Panel, PlotOptions,
Series, TextExt, Toggle, Widget,
};
use crate::app::{App, Transition};
@ -711,32 +711,16 @@ fn make_tabs(
ctx.style()
.btn_tab
.text(name)
// We use "disabled" to denote "currently selected", but we want to style it like
// normal
// We abuse "disabled" to denote "currently selected"
.disabled(current_tab.variant() == link.variant())
.bg_color(ctx.style().btn_tab.bg, ControlState::Disabled)
.label_color(ctx.style().btn_tab.fg, ControlState::Disabled)
.outline(ctx.style().btn_tab.outline, ControlState::Disabled)
// Hide the hit area for selectable tabs unless hovered
.bg_color(Color::CLEAR, ControlState::Default)
.outline((0.0, Color::CLEAR), ControlState::Default)
.bg_color(ctx.style().btn_tab.bg.alpha(0.6), ControlState::Hovered)
.build_def(ctx),
);
hyperlinks.insert(name.to_string(), link);
}
{
// stop-gap color that is semi-legible across themes until the tab redesign is completed
let tab_bg = ctx
.style()
.btn_tab
.bg
.lerp(ctx.style().btn_tab.fg, 0.3)
.alpha(1.0);
// TODO Centered, but actually, we need to set the padding of each button to divide the
// available space evenly. Fancy fill rules... hmmm.
Widget::custom_row(row).bg(tab_bg).margin_vert(16)
Widget::custom_row(row).margin_vert(16)
}
}

View File

@ -212,7 +212,7 @@ pub fn trips(
.to_geom(ctx, Some(0.3));
rows.push(
ctx.style()
.btn_floating
.btn_solid
.btn()
.custom_batch(row_btn.clone(), ControlState::Default)
.custom_batch(

View File

@ -4,7 +4,7 @@ use abstutil::prettyprint_usize;
use geom::{Duration, Time};
use sim::{TripEndpoint, TripID, TripMode};
use widgetry::table::{Col, Filter, Table};
use widgetry::{ControlState, EventCtx, Filler, Line, Panel, State, Text, Toggle, Widget};
use widgetry::{EventCtx, Filler, Line, Panel, State, Text, Toggle, Widget};
use crate::app::App;
use crate::common::{checkbox_per_mode, cmp_duration_shorter, color_for_mode};
@ -585,14 +585,11 @@ fn trip_category_selector(ctx: &mut EventCtx, app: &App, tab: DashTab) -> Widget
let total = finished + cancelled + unfinished;
let btn = |dash, action, label| {
let mut button = ctx.style().btn_tab.text(label);
if dash == tab {
button = button
.disabled(true)
.bg_color(ctx.style().btn_floating.bg, ControlState::Disabled)
.label_underlined_text(label);
}
button.build_widget(ctx, action)
ctx.style()
.btn_tab
.text(label)
.disabled(dash == tab)
.build_widget(ctx, action)
};
Widget::custom_row(vec![

View File

@ -106,15 +106,14 @@ impl<'a, 'c> Style {
pub fn btn_popup_icon_text(&self, icon_path: &'a str, text: &'a str) -> ButtonBuilder<'a, 'c> {
// The text is styled like an "outline" button, while the image is styled like a "solid"
// button.
let solid_style = &self.btn_tab;
self.btn_outline
.btn()
.label_text(text)
.image_path(icon_path)
.image_dims(25.0)
.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)
.image_bg_color(self.btn_solid.bg, ControlState::Default)
.image_bg_color(self.btn_solid.bg_hover, ControlState::Hovered)
// Move the padding from the *entire button* to just the image, so we get a colored
// padded area around the image.
.padding(0)

View File

@ -20,6 +20,7 @@ pub struct Style {
pub btn_plain: ButtonStyle,
pub btn_outline: ButtonStyle,
pub btn_floating: ButtonStyle,
pub btn_solid: ButtonStyle,
pub btn_tab: ButtonStyle,
pub btn_solid_destructive: ButtonStyle,
pub btn_plain_destructive: ButtonStyle,
@ -161,14 +162,15 @@ impl Style {
text_tooltip_color: Color::WHITE,
text_destructive_color: hex("#FF5E5E"),
btn_outline: ButtonStyle::outline_dark_fg(),
btn_solid: ButtonStyle::solid_light_fg(),
btn_plain: ButtonStyle::plain_dark_fg(),
btn_tab: ButtonStyle {
fg: Color::WHITE,
fg_disabled: Color::WHITE.alpha(0.3),
bg: hex("#4C4C4C").alpha(0.8),
bg_hover: hex("#4C4C4C"),
bg_disabled: Color::grey(0.6),
outline: (DEFAULT_OUTLINE_THICKNESS, hex("#4C4C4C").alpha(0.6)),
fg: hex("#4C4C4C").tint(0.2),
fg_disabled: hex("#4C4C4C"),
bg: Color::CLEAR,
bg_hover: hex("#4C4C4C").alpha(0.1),
bg_disabled: Color::WHITE,
outline: (0.0, Color::CLEAR),
},
btn_floating: ButtonStyle::solid_dark_fg(),
btn_solid_destructive: ButtonStyle::solid_destructive(),
@ -194,6 +196,7 @@ impl Style {
text_destructive_color: hex("#EB3223"),
btn_tab: ButtonStyle::solid_dark_fg(),
btn_outline: ButtonStyle::outline_light_fg(),
btn_solid: ButtonStyle::solid_dark_fg(),
btn_plain: ButtonStyle::plain_light_fg(),
btn_floating: ButtonStyle::solid_light_fg(),
btn_solid_destructive: ButtonStyle::solid_destructive(),
@ -220,8 +223,16 @@ impl Style {
text_tooltip_color: Color::WHITE,
text_destructive_color: hex("#FF5E5E"),
btn_outline: ButtonStyle::outline_light_fg(),
btn_solid: ButtonStyle::solid_dark_fg(),
btn_plain: ButtonStyle::plain_light_fg(),
btn_tab: ButtonStyle::solid_dark_fg(),
btn_tab: ButtonStyle {
fg: hex("#F2F2F2").shade(0.4),
fg_disabled: hex("#F2F2F2"),
bg: Color::CLEAR,
bg_hover: hex("#F2F2F2").alpha(0.1),
bg_disabled: navy,
outline: (0.0, Color::CLEAR),
},
btn_floating: ButtonStyle::solid_light_fg(),
btn_solid_destructive: ButtonStyle::solid_destructive(),
btn_plain_destructive: ButtonStyle::plain_destructive(),

View File

@ -179,7 +179,7 @@ fn make_btn(ctx: &EventCtx, label: &str, tooltip: &str, is_persisten_split: bool
// It's not ideal, but we only use one persistent split in the whole app
// and it's front and center - we'll notice if something breaks.
ctx.style()
.btn_tab
.btn_solid
.dropdown()
.padding(EdgeInsets {
top: 15.0,

View File

@ -41,7 +41,7 @@ impl<T: 'static> Menu<T> {
for (idx, choice) in self.choices.iter().enumerate() {
let is_hovered = idx == self.current_idx;
let mut text_color = if is_hovered {
choice.fg.unwrap_or(style.btn_tab.fg)
choice.fg.unwrap_or(style.btn_solid.fg)
} else {
choice.fg.unwrap_or(style.text_fg_color)
};
@ -71,7 +71,7 @@ impl<T: 'static> Menu<T> {
// TODO BG color should be on the TextSpan, so this isn't so terrible?
if is_hovered {
txt.highlight_last_line(style.btn_tab.bg);
txt.highlight_last_line(style.btn_solid.bg);
}
}
txt

View File

@ -37,6 +37,8 @@ pub mod tabs;
pub mod text_box;
pub mod toggle;
pub const DEFAULT_CORNER_RADIUS: f64 = 5.0;
/// Create a new widget by implementing this trait. You can instantiate your widget by calling
/// `Widget::new(Box::new(instance of your new widget))`, which gives you the usual style options.
pub trait WidgetImpl: downcast_rs::Downcast {
@ -341,7 +343,7 @@ impl Widget {
layout: LayoutStyle {
bg_color: None,
outline: None,
corner_rounding: CornerRounding::from(5.0),
corner_rounding: CornerRounding::from(DEFAULT_CORNER_RADIUS),
style: Style {
..Default::default()
},

View File

@ -115,9 +115,9 @@ impl Slider {
// The draggy thing
batch.push(
if self.mouse_on_slider {
ctx.style.btn_tab.bg_hover
ctx.style.btn_solid.bg_hover
} else {
ctx.style.btn_tab.bg
ctx.style.btn_solid.bg
},
self.button_geom(),
);
@ -140,12 +140,12 @@ impl Slider {
// The circle dragger
batch.push(
if self.mouse_on_slider {
ctx.style.btn_tab.bg_hover
ctx.style.btn_solid.bg_hover
} else {
// we don't want to use `ctx.style.btn_solid.bg` because it achieves it's
// "dulling" with opacity, which causes the slider to "peak through" and
// looks weird.
ctx.style.btn_tab.bg_hover.dull(0.2)
ctx.style.btn_solid.bg_hover.dull(0.2)
},
self.button_geom(),
);

View File

@ -1,4 +1,6 @@
use crate::widgets::DEFAULT_CORNER_RADIUS;
use crate::{ButtonBuilder, EventCtx, Panel, Widget};
use geom::CornerRadii;
struct Tab {
tab_id: String,
@ -18,6 +20,12 @@ impl Tab {
fn build_bar_item_widget(&self, ctx: &EventCtx, active: bool) -> Widget {
self.bar_item
.clone()
.corner_rounding(CornerRadii {
top_left: DEFAULT_CORNER_RADIUS,
top_right: DEFAULT_CORNER_RADIUS,
bottom_left: 0.0,
bottom_right: 0.0,
})
.disabled(active)
.build_widget(ctx, &self.tab_id)
}
@ -57,10 +65,16 @@ impl TabController {
/// A widget containing the tab bar and a content pane with the currently active tab.
pub fn build_widget(&mut self, ctx: &EventCtx) -> Widget {
Widget::col(vec![
Widget::custom_col(vec![
self.build_bar_items(ctx),
self.pop_active_content()
.container()
.corner_rounding(CornerRadii {
top_left: 0.0,
top_right: DEFAULT_CORNER_RADIUS,
bottom_left: DEFAULT_CORNER_RADIUS,
bottom_right: DEFAULT_CORNER_RADIUS,
})
.padding(16)
.bg(ctx.style().section_bg)
.named(self.active_content_id()),
@ -108,9 +122,9 @@ impl TabController {
.enumerate()
.map(|(idx, tab)| tab.build_bar_item_widget(ctx, idx == self.active_child))
.collect();
Widget::row(bar_items)
.container()
.bg(ctx.style().section_bg)
.named(self.bar_items_id())
}

View File

@ -48,16 +48,16 @@ impl Toggle {
let (label, bytes) = include_labeled_bytes!("../../icons/switch_off.svg");
let (batch, bounds) = load_svg_bytes(ctx.prerender, label, bytes).expect("invalid SVG");
let batch = batch
.color(RewriteColor::Change(Color::WHITE, ctx.style.btn_tab.bg))
.color(RewriteColor::Change(Color::BLACK, ctx.style.btn_tab.fg));
.color(RewriteColor::Change(Color::WHITE, ctx.style.btn_solid.bg))
.color(RewriteColor::Change(Color::BLACK, ctx.style.btn_solid.fg));
(batch, bounds)
};
let (on_batch, on_bounds) = {
let (label, bytes) = include_labeled_bytes!("../../icons/switch_on.svg");
let (batch, bounds) = load_svg_bytes(ctx.prerender, label, bytes).expect("invalid SVG");
let batch = batch
.color(RewriteColor::Change(Color::WHITE, ctx.style.btn_tab.bg))
.color(RewriteColor::Change(Color::BLACK, ctx.style.btn_tab.fg));
.color(RewriteColor::Change(Color::WHITE, ctx.style.btn_solid.bg))
.color(RewriteColor::Change(Color::BLACK, ctx.style.btn_solid.fg));
(batch, bounds)
};
@ -208,16 +208,16 @@ impl Toggle {
let (label, bytes) = include_labeled_bytes!("../../icons/toggle_left.svg");
let (batch, bounds) = load_svg_bytes(ctx.prerender, label, bytes).expect("invalid SVG");
let batch = batch
.color(RewriteColor::Change(Color::WHITE, ctx.style.btn_tab.bg))
.color(RewriteColor::Change(Color::BLACK, ctx.style.btn_tab.fg));
.color(RewriteColor::Change(Color::WHITE, ctx.style.btn_solid.bg))
.color(RewriteColor::Change(Color::BLACK, ctx.style.btn_solid.fg));
(batch, bounds)
};
let (right_batch, right_bounds) = {
let (label, bytes) = include_labeled_bytes!("../../icons/toggle_right.svg");
let (batch, bounds) = load_svg_bytes(ctx.prerender, label, bytes).expect("invalid SVG");
let batch = batch
.color(RewriteColor::Change(Color::WHITE, ctx.style.btn_tab.bg))
.color(RewriteColor::Change(Color::BLACK, ctx.style.btn_tab.fg));
.color(RewriteColor::Change(Color::WHITE, ctx.style.btn_solid.bg))
.color(RewriteColor::Change(Color::BLACK, ctx.style.btn_solid.fg));
(batch, bounds)
};

View File

@ -25,6 +25,8 @@ pub fn main() {
|ctx| {
// TODO: remove Style::pregame and make light_bg the default.
ctx.set_style(widgetry::Style::light_bg());
// TODO: Add a toggle to switch theme in demo (and recreate UI in that new theme)
// ctx.set_style(widgetry::Style::dark_bg());
(App {}, vec![Box::new(Demo::new(ctx))])
},
@ -332,42 +334,45 @@ fn make_tabs(ctx: &mut EventCtx) -> TabController {
style
.btn_solid_primary
.text("Primary")
.build_widget(ctx, "btn_primary_text"),
.build_widget(ctx, "btn_solid_primary_text"),
Widget::row(vec![
style
.btn_solid_primary
.icon("system/assets/tools/map.svg")
.build_widget(ctx, "btn_primary_icon_1"),
.build_widget(ctx, "btn_solid_primary_icon"),
style
.btn_plain_primary
.icon("system/assets/tools/map.svg")
.build_widget(ctx, "btn_primary_icon_2"),
.build_widget(ctx, "btn_plain_primary_icon"),
]),
style
.btn_solid_primary
.icon_text("system/assets/tools/location.svg", "Primary")
.build_widget(ctx, "btn_primary_icon_text"),
.build_widget(ctx, "btn_solid_primary_icon_text"),
]),
Widget::row(vec![
style
.btn_outline
.text("Secondary")
.build_widget(ctx, "btn_outline_dark_text"),
.build_widget(ctx, "btn_outline_text"),
Widget::row(vec![
style
.btn_outline
.icon("system/assets/tools/map.svg")
.build_widget(ctx, "btn_outline_dark_icon_1"),
.build_widget(ctx, "btn_outline_icon"),
style
.btn_plain
.icon("system/assets/tools/map.svg")
.build_widget(ctx, "btn_outline_dark_icon_2"),
.build_widget(ctx, "btn_plain_icon"),
]),
style
.btn_outline
.icon_text("system/assets/tools/home.svg", "Secondary")
.build_widget(ctx, "btn_outline.icon_text"),
]),
Widget::row(vec![style
.btn_popup_icon_text("system/assets/tools/map.svg", "Popup")
.build_widget(ctx, "btn_popup_icon_text")]),
Text::from_multiline(vec![
Line("Images").big_heading_styled().size(18),
Line(
@ -502,95 +507,99 @@ fn make_tabs(ctx: &mut EventCtx) -> TabController {
fn make_controls(ctx: &mut EventCtx, tabs: &mut TabController) -> Panel {
Panel::new(Widget::col(vec![
Text::from_multiline(vec![
Line("widgetry demo").big_heading_styled(),
Line("Click and drag the background to pan, use touchpad or scroll wheel to zoom"),
])
.into_widget(ctx),
Widget::row(vec![
ctx.style()
.btn_outline
.text("New faces")
.hotkey(Key::F)
.build_widget(ctx, "generate new faces"),
Toggle::switch(ctx, "Draw scrollable canvas", None, true),
Toggle::switch(ctx, "Show timeseries", lctrl(Key::T), false),
]),
"Stopwatch: ..."
.text_widget(ctx)
.named("stopwatch")
Text::from(Line("widgetry demo").big_heading_styled()).into_widget(ctx),
Widget::col(vec![
Text::from(Line(
"Click and drag the background to pan, use touchpad or scroll wheel to zoom",
))
.into_widget(ctx),
Widget::row(vec![
ctx.style()
.btn_outline
.text("New faces")
.hotkey(Key::F)
.build_widget(ctx, "generate new faces"),
Toggle::switch(ctx, "Draw scrollable canvas", None, true),
Toggle::switch(ctx, "Show timeseries", lctrl(Key::T), false),
]),
"Stopwatch: ..."
.text_widget(ctx)
.named("stopwatch")
.margin_above(30),
Widget::row(vec![
Toggle::new(
false,
ctx.style()
.btn_outline
.text("Pause")
.hotkey(Key::Space)
.build(ctx, "pause the stopwatch"),
ctx.style()
.btn_outline
.text("Resume")
.hotkey(Key::Space)
.build(ctx, "resume the stopwatch"),
)
.named("paused"),
PersistentSplit::widget(
ctx,
"adjust timer",
Duration::seconds(20.0),
None,
vec![
Choice::new("+20s", Duration::seconds(20.0)),
Choice::new("-10s", Duration::seconds(-10.0)),
],
),
ctx.style()
.btn_outline
.text("Reset Timer")
.build_widget(ctx, "reset the stopwatch"),
])
.evenly_spaced(),
Widget::row(vec![
Widget::dropdown(
ctx,
"alignment",
(HorizontalAlignment::Center, VerticalAlignment::Top),
vec![
Choice::new("Top", (HorizontalAlignment::Center, VerticalAlignment::Top)),
Choice::new(
"Left",
(HorizontalAlignment::Left, VerticalAlignment::Center),
),
Choice::new(
"Bottom",
(HorizontalAlignment::Center, VerticalAlignment::Bottom),
),
Choice::new(
"Right",
(HorizontalAlignment::Right, VerticalAlignment::Center),
),
Choice::new(
"Center",
(HorizontalAlignment::Center, VerticalAlignment::Center),
),
],
),
Widget::dropdown(
ctx,
"texture",
(Texture::SAND, Texture::CACTUS),
vec![
Choice::new("Cold", (Texture::SNOW, Texture::SNOW_PERSON)),
Choice::new("Hot", (Texture::SAND, Texture::CACTUS)),
],
),
ctx.style()
.btn_solid_primary
.text("Apply")
.build_widget(ctx, "apply"),
])
.margin_above(30),
Widget::row(vec![
Toggle::new(
false,
ctx.style()
.btn_outline
.text("Pause")
.hotkey(Key::Space)
.build(ctx, "pause the stopwatch"),
ctx.style()
.btn_outline
.text("Resume")
.hotkey(Key::Space)
.build(ctx, "resume the stopwatch"),
)
.named("paused"),
PersistentSplit::widget(
ctx,
"adjust timer",
Duration::seconds(20.0),
None,
vec![
Choice::new("+20s", Duration::seconds(20.0)),
Choice::new("-10s", Duration::seconds(-10.0)),
],
),
ctx.style()
.btn_outline
.text("Reset Timer")
.build_widget(ctx, "reset the stopwatch"),
])
.evenly_spaced(),
Widget::row(vec![
Widget::dropdown(
ctx,
"alignment",
(HorizontalAlignment::Center, VerticalAlignment::Top),
vec![
Choice::new("Top", (HorizontalAlignment::Center, VerticalAlignment::Top)),
Choice::new(
"Left",
(HorizontalAlignment::Left, VerticalAlignment::Center),
),
Choice::new(
"Bottom",
(HorizontalAlignment::Center, VerticalAlignment::Bottom),
),
Choice::new(
"Right",
(HorizontalAlignment::Right, VerticalAlignment::Center),
),
Choice::new(
"Center",
(HorizontalAlignment::Center, VerticalAlignment::Center),
),
],
),
Widget::dropdown(
ctx,
"texture",
(Texture::SAND, Texture::CACTUS),
vec![
Choice::new("Cold", (Texture::SNOW, Texture::SNOW_PERSON)),
Choice::new("Hot", (Texture::SAND, Texture::CACTUS)),
],
),
ctx.style()
.btn_solid_primary
.text("Apply")
.build_widget(ctx, "apply"),
])
.margin_above(30),
.padding(16)
.bg(ctx.style().section_bg),
tabs.build_widget(ctx),
])) // end panel
.aligned(HorizontalAlignment::Center, VerticalAlignment::Top)