[refactor] break up Style into Spacing, Interaction and Visuals

Also make sliders fixed-width
This commit is contained in:
Emil Ernerfeldt 2020-08-31 22:27:31 +02:00
parent fe50f39590
commit 413ed6999e
15 changed files with 464 additions and 271 deletions

View file

@ -182,15 +182,15 @@ impl CollapsingHeader {
let id = ui.make_unique_child_id_full(id_source, Some(title)); let id = ui.make_unique_child_id_full(id_source, Some(title));
let available = ui.available_finite(); let available = ui.available_finite();
let text_pos = available.min + vec2(ui.style().indent, 0.0); let text_pos = available.min + vec2(ui.style().spacing.indent, 0.0);
let galley = label.layout_width(ui, available.width() - ui.style().indent); let galley = label.layout_width(ui, available.width() - ui.style().spacing.indent);
let text_max_x = text_pos.x + galley.size.x; let text_max_x = text_pos.x + galley.size.x;
let desired_width = text_max_x - available.left(); let desired_width = text_max_x - available.left();
let desired_width = desired_width.max(available.width()); let desired_width = desired_width.max(available.width());
let size = vec2( let size = vec2(
desired_width, desired_width,
galley.size.y + 2.0 * ui.style().button_padding.y, galley.size.y + 2.0 * ui.style().spacing.button_padding.y,
); );
let rect = ui.allocate_space(size); let rect = ui.allocate_space(size);
@ -205,9 +205,9 @@ impl CollapsingHeader {
let bg_index = ui.painter().add(PaintCmd::Noop); let bg_index = ui.painter().add(PaintCmd::Noop);
{ {
let (mut icon_rect, _) = ui.style().icon_rectangles(response.rect); let (mut icon_rect, _) = ui.style().spacing.icon_rectangles(response.rect);
icon_rect.set_center(pos2( icon_rect.set_center(pos2(
response.rect.left() + ui.style().indent / 2.0, response.rect.left() + ui.style().spacing.indent / 2.0,
response.rect.center().y, response.rect.center().y,
)); ));
let icon_response = Response { let icon_response = Response {

View file

@ -15,10 +15,10 @@ pub struct Frame {
impl Frame { impl Frame {
pub fn window(style: &Style) -> Self { pub fn window(style: &Style) -> Self {
Self { Self {
margin: style.window_padding, margin: style.spacing.window_padding,
corner_radius: style.window.corner_radius, corner_radius: style.visuals.window_corner_radius,
fill: Some(style.background_fill), fill: Some(style.visuals.background_fill),
outline: style.interact.inactive.bg_outline, // because we can resize windows outline: style.visuals.interacted.inactive.bg_outline, // because we can resize windows
} }
} }
@ -35,16 +35,16 @@ impl Frame {
Self { Self {
margin: Vec2::splat(1.0), margin: Vec2::splat(1.0),
corner_radius: 2.0, corner_radius: 2.0,
fill: Some(style.background_fill), fill: Some(style.visuals.background_fill),
outline: Some(LineStyle::new(1.0, Srgba::gray(128))), outline: Some(LineStyle::new(1.0, Srgba::gray(128))),
} }
} }
pub fn popup(style: &Style) -> Self { pub fn popup(style: &Style) -> Self {
Self { Self {
margin: style.window_padding, margin: style.spacing.window_padding,
corner_radius: 5.0, corner_radius: 5.0,
fill: Some(style.background_fill), fill: Some(style.visuals.background_fill),
outline: Some(LineStyle::new(1.0, Srgba::gray(128))), outline: Some(LineStyle::new(1.0, Srgba::gray(128))),
} }
} }

View file

@ -145,7 +145,7 @@ impl Resize {
let corner_response = if self.resizable { let corner_response = if self.resizable {
// Resize-corner: // Resize-corner:
let corner_size = Vec2::splat(ui.style().resize_corner_size); let corner_size = Vec2::splat(ui.style().visuals.resize_corner_size);
let corner_rect = let corner_rect =
Rect::from_min_size(position + state.desired_size - corner_size, corner_size); Rect::from_min_size(position + state.desired_size - corner_size, corner_size);
let corner_response = ui.interact(corner_rect, id.with("corner"), Sense::drag()); let corner_response = ui.interact(corner_rect, id.with("corner"), Sense::drag());
@ -169,7 +169,7 @@ impl Resize {
let inner_rect = Rect::from_min_size(position, state.desired_size); let inner_rect = Rect::from_min_size(position, state.desired_size);
let mut content_clip_rect = inner_rect.expand(ui.style().clip_rect_margin); let mut content_clip_rect = inner_rect.expand(ui.style().visuals.clip_rect_margin);
// If we pull the resize handle to shrink, we want to TRY to shrink it. // If we pull the resize handle to shrink, we want to TRY to shrink it.
// After laying out the contents, we might be much bigger. // After laying out the contents, we might be much bigger.
@ -177,7 +177,9 @@ impl Resize {
// then we will clip the contents of the region even thought the result gets larger. This is simply ugly! // then we will clip the contents of the region even thought the result gets larger. This is simply ugly!
// So we use the memory of last_content_size to make the clip rect large enough. // So we use the memory of last_content_size to make the clip rect large enough.
content_clip_rect.max = content_clip_rect.max.max( content_clip_rect.max = content_clip_rect.max.max(
inner_rect.min + state.last_content_size + Vec2::splat(ui.style().clip_rect_margin), inner_rect.min
+ state.last_content_size
+ Vec2::splat(ui.style().visuals.clip_rect_margin),
); );
content_clip_rect = content_clip_rect.intersect(ui.clip_rect()); // Respect parent region content_clip_rect = content_clip_rect.intersect(ui.clip_rect()); // Respect parent region
@ -236,7 +238,7 @@ impl Resize {
rect, rect,
corner_radius: 3.0, corner_radius: 3.0,
fill: None, fill: None,
outline: Some(ui.style().thin_outline), outline: Some(ui.style().visuals.thin_outline),
}); });
} }
@ -250,7 +252,7 @@ impl Resize {
ui.memory().resize.insert(id, state); ui.memory().resize.insert(id, state);
if ui.ctx().style().debug_resize { if ui.ctx().style().visuals.debug_resize {
ui.ctx().debug_painter().debug_rect( ui.ctx().debug_painter().debug_rect(
Rect::from_min_size(content_ui.top_left(), state.desired_size), Rect::from_min_size(content_ui.top_left(), state.desired_size),
color::GREEN, color::GREEN,

View file

@ -99,7 +99,7 @@ impl ScrollArea {
inner_rect.min - state.offset, inner_rect.min - state.offset,
vec2(inner_size.x, f32::INFINITY), vec2(inner_size.x, f32::INFINITY),
)); ));
let mut content_clip_rect = inner_rect.expand(ui.style().clip_rect_margin); let mut content_clip_rect = inner_rect.expand(ui.style().visuals.clip_rect_margin);
content_clip_rect = content_clip_rect.intersect(ui.clip_rect()); content_clip_rect = content_clip_rect.intersect(ui.clip_rect());
content_clip_rect.max.x = ui.clip_rect().max.x - current_scroll_bar_width; // Nice handling of forced resizing beyond the possible content_clip_rect.max.x = ui.clip_rect().max.x - current_scroll_bar_width; // Nice handling of forced resizing beyond the possible
content_ui.set_clip_rect(content_clip_rect); content_ui.set_clip_rect(content_clip_rect);
@ -175,7 +175,7 @@ impl Prepared {
if current_scroll_bar_width > 0.0 { if current_scroll_bar_width > 0.0 {
let animation_t = current_scroll_bar_width / max_scroll_bar_width; let animation_t = current_scroll_bar_width / max_scroll_bar_width;
// margin between contents and scroll bar // margin between contents and scroll bar
let margin = animation_t * ui.style().item_spacing.x; let margin = animation_t * ui.style().spacing.item_spacing.x;
let left = inner_rect.right() + margin; let left = inner_rect.right() + margin;
let right = outer_rect.right(); let right = outer_rect.right();
let corner_radius = (right - left) / 2.0; let corner_radius = (right - left) / 2.0;
@ -243,7 +243,7 @@ impl Prepared {
ui.painter().add(paint::PaintCmd::Rect { ui.painter().add(paint::PaintCmd::Rect {
rect: outer_scroll_rect, rect: outer_scroll_rect,
corner_radius, corner_radius,
fill: Some(ui.style().dark_bg_color), fill: Some(ui.style().visuals.dark_bg_color),
outline: None, outline: None,
}); });
@ -274,5 +274,5 @@ impl Prepared {
} }
fn max_scroll_bar_width_with_margin(ui: &Ui) -> f32 { fn max_scroll_bar_width_with_margin(ui: &Ui) -> f32 {
ui.style().item_spacing.x + 16.0 ui.style().spacing.item_spacing.x + 16.0
} }

View file

@ -202,7 +202,7 @@ impl<'open> Window<'open> {
let last_frame_outer_rect = area.state().rect(); let last_frame_outer_rect = area.state().rect();
let interaction = if possible.movable || possible.resizable { let interaction = if possible.movable || possible.resizable {
let title_bar_height = let title_bar_height =
title_label.font_height(ctx.fonts()) + 1.0 * ctx.style().item_spacing.y; // this could be better title_label.font_height(ctx.fonts()) + 1.0 * ctx.style().spacing.item_spacing.y; // this could be better
let margins = 2.0 * frame.margin + vec2(0.0, title_bar_height); let margins = 2.0 * frame.margin + vec2(0.0, title_bar_height);
window_interaction( window_interaction(
@ -255,7 +255,7 @@ impl<'open> Window<'open> {
.add_contents(&mut frame.content_ui, collapsing_id, |ui| { .add_contents(&mut frame.content_ui, collapsing_id, |ui| {
resize.show(ui, |ui| { resize.show(ui, |ui| {
// Add some spacing between title and content: // Add some spacing between title and content:
ui.allocate_space(ui.style().item_spacing); ui.allocate_space(ui.style().spacing.item_spacing);
if let Some(scroll) = scroll { if let Some(scroll) = scroll {
scroll.show(ui, add_contents) scroll.show(ui, add_contents)
@ -294,14 +294,14 @@ impl<'open> Window<'open> {
&mut area_content_ui, &mut area_content_ui,
outer_rect, outer_rect,
interaction, interaction,
ctx.style().interact.active, ctx.style().visuals.interacted.active,
); );
} else if let Some(hover_interaction) = hover_interaction { } else if let Some(hover_interaction) = hover_interaction {
paint_frame_interaction( paint_frame_interaction(
&mut area_content_ui, &mut area_content_ui,
outer_rect, outer_rect,
hover_interaction, hover_interaction,
ctx.style().interact.hovered, ctx.style().visuals.interacted.hovered,
); );
} }
} }
@ -312,7 +312,7 @@ impl<'open> Window<'open> {
} }
fn paint_resize_corner(ui: &mut Ui, outer_rect: Rect, frame_outline: Option<LineStyle>) { fn paint_resize_corner(ui: &mut Ui, outer_rect: Rect, frame_outline: Option<LineStyle>) {
let corner_size = Vec2::splat(ui.style().resize_corner_size); let corner_size = Vec2::splat(ui.style().visuals.resize_corner_size);
let handle_offset = -Vec2::splat(2.0); let handle_offset = -Vec2::splat(2.0);
let corner_rect = let corner_rect =
Rect::from_min_size(outer_rect.max - corner_size + handle_offset, corner_size); Rect::from_min_size(outer_rect.max - corner_size + handle_offset, corner_size);
@ -468,32 +468,32 @@ fn resize_hover(
return None; return None;
} }
let side_interact_radius = ctx.style().resize_interact_radius_side; let side_grab_radius = ctx.style().interaction.resize_grab_radius_side;
let corner_interact_radius = ctx.style().resize_interact_radius_corner; let corner_grab_radius = ctx.style().interaction.resize_grab_radius_corner;
if rect.expand(side_interact_radius).contains(mouse_pos) { if rect.expand(side_grab_radius).contains(mouse_pos) {
let (mut left, mut right, mut top, mut bottom) = Default::default(); let (mut left, mut right, mut top, mut bottom) = Default::default();
if possible.resizable { if possible.resizable {
right = (rect.right() - mouse_pos.x).abs() <= side_interact_radius; right = (rect.right() - mouse_pos.x).abs() <= side_grab_radius;
bottom = (rect.bottom() - mouse_pos.y).abs() <= side_interact_radius; bottom = (rect.bottom() - mouse_pos.y).abs() <= side_grab_radius;
if rect.right_bottom().distance(mouse_pos) < corner_interact_radius { if rect.right_bottom().distance(mouse_pos) < corner_grab_radius {
right = true; right = true;
bottom = true; bottom = true;
} }
if possible.movable { if possible.movable {
left = (rect.left() - mouse_pos.x).abs() <= side_interact_radius; left = (rect.left() - mouse_pos.x).abs() <= side_grab_radius;
top = (rect.top() - mouse_pos.y).abs() <= side_interact_radius; top = (rect.top() - mouse_pos.y).abs() <= side_grab_radius;
if rect.right_top().distance(mouse_pos) < corner_interact_radius { if rect.right_top().distance(mouse_pos) < corner_grab_radius {
right = true; right = true;
top = true; top = true;
} }
if rect.left_top().distance(mouse_pos) < corner_interact_radius { if rect.left_top().distance(mouse_pos) < corner_grab_radius {
left = true; left = true;
top = true; top = true;
} }
if rect.left_bottom().distance(mouse_pos) < corner_interact_radius { if rect.left_bottom().distance(mouse_pos) < corner_grab_radius {
left = true; left = true;
bottom = true; bottom = true;
} }
@ -530,9 +530,9 @@ fn paint_frame_interaction(
ui: &mut Ui, ui: &mut Ui,
rect: Rect, rect: Rect,
interaction: WindowInteraction, interaction: WindowInteraction,
style: style::WidgetStyle, visuals: style::WidgetVisuals,
) { ) {
let cr = ui.style().window.corner_radius; let cr = ui.style().visuals.window_corner_radius;
let Rect { min, max } = rect; let Rect { min, max } = rect;
let mut path = Path::default(); let mut path = Path::default();
@ -567,7 +567,7 @@ fn paint_frame_interaction(
path, path,
closed: false, closed: false,
fill: None, fill: None,
outline: style.bg_outline, outline: visuals.bg_outline,
}); });
} }
@ -591,8 +591,8 @@ fn show_title_bar(
let title_bar_and_rect = ui.horizontal_centered(|ui| { let title_bar_and_rect = ui.horizontal_centered(|ui| {
ui.set_desired_height(title_label.font_height(ui.fonts())); ui.set_desired_height(title_label.font_height(ui.fonts()));
let item_spacing = ui.style().item_spacing; let item_spacing = ui.style().spacing.item_spacing;
let button_size = ui.style().start_icon_width; let button_size = ui.style().spacing.icon_width;
if collapsible { if collapsible {
// TODO: make clickable radius larger // TODO: make clickable radius larger
@ -668,10 +668,10 @@ impl TitleBar {
// paint separator between title and content: // paint separator between title and content:
let left = outer_rect.left(); let left = outer_rect.left();
let right = outer_rect.right(); let right = outer_rect.right();
let y = content_rect.top() + ui.style().item_spacing.y * 0.5; let y = content_rect.top() + ui.style().spacing.item_spacing.y * 0.5;
ui.painter().line_segment( ui.painter().line_segment(
[pos2(left, y), pos2(right, y)], [pos2(left, y), pos2(right, y)],
ui.style().interact.inactive.bg_outline.unwrap(), ui.style().visuals.interacted.inactive.bg_outline.unwrap(),
); );
} }
@ -686,10 +686,10 @@ impl TitleBar {
} }
fn close_button_ui(&self, ui: &mut Ui) -> Response { fn close_button_ui(&self, ui: &mut Ui) -> Response {
let button_size = ui.style().start_icon_width; let button_size = ui.style().spacing.icon_width;
let button_rect = Rect::from_min_size( let button_rect = Rect::from_min_size(
pos2( pos2(
self.rect.right() - ui.style().item_spacing.x - button_size, self.rect.right() - ui.style().spacing.item_spacing.x - button_size,
self.rect.center().y - 0.5 * button_size, self.rect.center().y - 0.5 * button_size,
), ),
Vec2::splat(button_size), Vec2::splat(button_size),

View file

@ -337,8 +337,8 @@ impl Context {
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
pub fn layer_at(&self, pos: Pos2) -> Option<Layer> { pub fn layer_at(&self, pos: Pos2) -> Option<Layer> {
let resize_interact_radius_side = self.style().resize_interact_radius_side; let resize_grab_radius_side = self.style().interaction.resize_grab_radius_side;
self.memory().layer_at(pos, resize_interact_radius_side) self.memory().layer_at(pos, resize_grab_radius_side)
} }
pub fn contains_mouse(&self, layer: Layer, clip_rect: Rect, rect: Rect) -> bool { pub fn contains_mouse(&self, layer: Layer, clip_rect: Rect, rect: Rect) -> bool {
@ -359,7 +359,7 @@ impl Context {
interaction_id: Option<Id>, interaction_id: Option<Id>,
sense: Sense, sense: Sense,
) -> Response { ) -> Response {
let interact_rect = rect.expand2(0.5 * self.style().item_spacing); // make it easier to click. TODO: nice way to do this let interact_rect = rect.expand2(0.5 * self.style().spacing.item_spacing); // make it easier to click. TODO: nice way to do this
let hovered = self.contains_mouse(layer, clip_rect, interact_rect); let hovered = self.contains_mouse(layer, clip_rect, interact_rect);
let has_kb_focus = interaction_id let has_kb_focus = interaction_id
.map(|id| self.memory().has_kb_focus(id)) .map(|id| self.memory().has_kb_focus(id))

View file

@ -473,7 +473,7 @@ impl Default for Widgets {
impl Widgets { impl Widgets {
pub fn ui(&mut self, ui: &mut Ui) { pub fn ui(&mut self, ui: &mut Ui) {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.style_mut().item_spacing.x = 0.0; ui.style_mut().spacing.item_spacing.x = 0.0;
ui.add(label!("Text can have ").text_color(srgba(110, 255, 110, 255))); ui.add(label!("Text can have ").text_color(srgba(110, 255, 110, 255)));
ui.add(label!("color ").text_color(srgba(128, 140, 255, 255))); ui.add(label!("color ").text_color(srgba(128, 140, 255, 255)));
ui.add(label!("and tooltips")).tooltip_text( ui.add(label!("and tooltips")).tooltip_text(
@ -520,7 +520,7 @@ impl Widgets {
{ {
ui.label("An angle stored as radians, but edited in degrees:"); ui.label("An angle stored as radians, but edited in degrees:");
ui.horizontal_centered(|ui| { ui.horizontal_centered(|ui| {
ui.style_mut().item_spacing.x = 0.0; ui.style_mut().spacing.item_spacing.x = 0.0;
ui.drag_angle(&mut self.angle); ui.drag_angle(&mut self.angle);
ui.label(format!(" = {} radians", self.angle)); ui.label(format!(" = {} radians", self.angle));
}); });

View file

@ -4,7 +4,7 @@ use crate::{paint::PaintCmd, *};
// iOS style toggle switch // iOS style toggle switch
pub fn toggle(ui: &mut Ui, on: &mut bool) -> Response { pub fn toggle(ui: &mut Ui, on: &mut bool) -> Response {
// First we must reserve some space to use: // First we must reserve some space to use:
let desired_size = vec2(2.0, 1.0) * ui.style().clickable_diameter; let desired_size = vec2(2.0, 1.0) * ui.style().spacing.clickable_diameter;
let rect = ui.allocate_space(desired_size); let rect = ui.allocate_space(desired_size);
// Now that we have an area, we want to check for clicks. // Now that we have an area, we want to check for clicks.

View file

@ -184,7 +184,7 @@ impl Layout {
} }
cursor_change.x += child_size.x; cursor_change.x += child_size.x;
cursor_change.x += style.item_spacing.x; // Where to put next thing, if there is a next thing cursor_change.x += style.spacing.item_spacing.x; // Where to put next thing, if there is a next thing
} else { } else {
if let Some(align) = self.align { if let Some(align) = self.align {
child_move.x += match align { child_move.x += match align {
@ -197,7 +197,7 @@ impl Layout {
child_size.x = child_size.x.max(available_size.x); child_size.x = child_size.x.max(available_size.x);
}; };
cursor_change.y += child_size.y; cursor_change.y += child_size.y;
cursor_change.y += style.item_spacing.y; // Where to put next thing, if there is a next thing cursor_change.y += style.spacing.item_spacing.y; // Where to put next thing, if there is a next thing
} }
if self.is_reversed() { if self.is_reversed() {

View file

@ -61,17 +61,17 @@ pub fn bar<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Rect)
ui.horizontal_centered(|ui| { ui.horizontal_centered(|ui| {
Frame::menu_bar(ui.style()).show(ui, |ui| { Frame::menu_bar(ui.style()).show(ui, |ui| {
let mut style = ui.style().clone(); let mut style = ui.style().clone();
style.button_padding = vec2(2.0, 0.0); style.spacing.button_padding = vec2(2.0, 0.0);
// style.interact.active.bg_fill = None; // style.visuals.interacted.active.bg_fill = None;
style.interact.active.bg_outline = None; style.visuals.interacted.active.bg_outline = None;
// style.interact.hovered.bg_fill = None; // style.visuals.interacted.hovered.bg_fill = None;
style.interact.hovered.bg_outline = None; style.visuals.interacted.hovered.bg_outline = None;
style.interact.inactive.bg_fill = None; style.visuals.interacted.inactive.bg_fill = None;
style.interact.inactive.bg_outline = None; style.visuals.interacted.inactive.bg_outline = None;
ui.set_style(style); ui.set_style(style);
// Take full width and fixed height: // Take full width and fixed height:
let height = ui.style().menu_bar.height; let height = ui.style().spacing.menu_bar_height;
ui.set_desired_height(height); ui.set_desired_height(height);
ui.expand_to_size(vec2(ui.available().width(), height)); ui.expand_to_size(vec2(ui.available().width(), height));
@ -108,7 +108,7 @@ fn menu_impl<'c>(
let mut button = Button::new(title); let mut button = Button::new(title);
if bar_state.open_menu == Some(menu_id) { if bar_state.open_menu == Some(menu_id) {
button = button.fill(Some(ui.style().interact.active.main_fill)); button = button.fill(Some(ui.style().visuals.interacted.active.main_fill));
} }
let button_response = ui.add(button); let button_response = ui.add(button);
@ -127,13 +127,13 @@ fn menu_impl<'c>(
frame.show(ui, |ui| { frame.show(ui, |ui| {
resize.show(ui, |ui| { resize.show(ui, |ui| {
let mut style = ui.style().clone(); let mut style = ui.style().clone();
style.button_padding = vec2(2.0, 0.0); style.spacing.button_padding = vec2(2.0, 0.0);
// style.interact.active.bg_fill = None; // style.visuals.interacted.active.bg_fill = None;
style.interact.active.bg_outline = None; style.visuals.interacted.active.bg_outline = None;
// style.interact.hovered.bg_fill = None; // style.visuals.interacted.hovered.bg_fill = None;
style.interact.hovered.bg_outline = None; style.visuals.interacted.hovered.bg_outline = None;
style.interact.inactive.bg_fill = None; style.visuals.interacted.inactive.bg_fill = None;
style.interact.inactive.bg_outline = None; style.visuals.interacted.inactive.bg_outline = None;
ui.set_style(style); ui.set_style(style);
ui.set_layout(Layout::justified(Direction::Vertical)); ui.set_layout(Layout::justified(Direction::Vertical));
add_contents(ui) add_contents(ui)

View file

@ -2,43 +2,86 @@
use crate::{color::*, math::*, paint::LineStyle, types::*}; use crate::{color::*, math::*, paint::LineStyle, types::*};
// TODO: split into Spacing and Style?
/// Specifies the look and feel of a `Ui`. /// Specifies the look and feel of a `Ui`.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Style { pub struct Style {
pub spacing: Spacing,
pub interaction: Interaction,
pub visuals: Visuals,
/// How many seconds a typical animation should last
pub animation_time: f32,
}
impl Style {
// TODO: rename style.interact() to maybe... `style.response_visuals` ?
/// Use this style for interactive things
pub fn interact(&self, response: &Response) -> &WidgetVisuals {
self.visuals.interacted.style(response)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Spacing {
/// Horizontal and vertical spacing between widgets
pub item_spacing: Vec2,
/// Horizontal and vertical padding within a window frame. /// Horizontal and vertical padding within a window frame.
pub window_padding: Vec2, pub window_padding: Vec2,
/// Button size is text size plus this on each side /// Button size is text size plus this on each side
pub button_padding: Vec2, pub button_padding: Vec2,
/// Horizontal and vertical spacing between widgets
pub item_spacing: Vec2,
/// Indent collapsing regions etc by this much. /// Indent collapsing regions etc by this much.
pub indent: f32, pub indent: f32,
/// Anything clickable is (at least) this wide. /// Anything clickable is (at least) this wide.
pub clickable_diameter: f32, pub clickable_diameter: f32,
/// Total width of a slider
pub slider_width: f32,
/// Checkboxes, radio button and collapsing headers have an icon at the start. /// Checkboxes, radio button and collapsing headers have an icon at the start.
/// The text starts after this many pixels. /// The text starts after this many pixels.
pub start_icon_width: f32, pub icon_width: f32,
pub menu_bar_height: f32,
}
impl Spacing {
/// Returns small icon rectangle and big icon rectangle
pub fn icon_rectangles(&self, rect: Rect) -> (Rect, Rect) {
let box_side = self.icon_width;
let big_icon_rect = Rect::from_center_size(
pos2(rect.left() + box_side / 2.0, rect.center().y),
vec2(box_side, box_side),
);
let small_rect_side = 8.0; // TODO: make a parameter
let small_icon_rect =
Rect::from_center_size(big_icon_rect.center(), Vec2::splat(small_rect_side));
(small_icon_rect, big_icon_rect)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Interaction {
/// Mouse must be the close to the side of a window to resize /// Mouse must be the close to the side of a window to resize
pub resize_interact_radius_side: f32, pub resize_grab_radius_side: f32,
/// Mouse must be the close to the corner of a window to resize /// Mouse must be the close to the corner of a window to resize
pub resize_interact_radius_corner: f32, pub resize_grab_radius_corner: f32,
}
pub resize_corner_size: f32, #[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Visuals {
pub interacted: Interacted,
// -----------------------------------------------
// Purely visual:
pub interact: Interact,
// TODO: an WidgetStyle ?
pub text_color: Srgba, pub text_color: Srgba,
/// For stuff like check marks in check boxes. /// For stuff like check marks in check boxes.
@ -52,18 +95,14 @@ pub struct Style {
/// e.g. the background of the slider or text edit /// e.g. the background of the slider or text edit
pub dark_bg_color: Srgba, pub dark_bg_color: Srgba,
pub window_corner_radius: f32,
pub resize_corner_size: f32,
/// Blink text cursor by this frequency. If None, always show the cursor. /// Blink text cursor by this frequency. If None, always show the cursor.
pub cursor_blink_hz: Option<f32>, pub cursor_blink_hz: Option<f32>,
pub text_cursor_width: f32, pub text_cursor_width: f32,
// TODO: add ability to disable animations!
/// How many seconds a typical animation should last
pub animation_time: f32,
pub window: Window,
pub menu_bar: MenuBar,
/// Allow child widgets to be just on the border and still have an outline with some thickness /// Allow child widgets to be just on the border and still have an outline with some thickness
pub clip_rect_margin: f32, pub clip_rect_margin: f32,
@ -73,86 +112,17 @@ pub struct Style {
pub debug_resize: bool, pub debug_resize: bool,
} }
impl Default for Style {
fn default() -> Self {
Self {
window_padding: vec2(6.0, 6.0),
button_padding: vec2(4.0, 1.0),
item_spacing: vec2(8.0, 4.0),
indent: 21.0,
clickable_diameter: 22.0,
start_icon_width: 14.0,
resize_interact_radius_side: 5.0,
resize_interact_radius_corner: 10.0,
resize_corner_size: 16.0,
interact: Default::default(),
text_color: Srgba::gray(160),
line_width: 1.0,
thin_outline: LineStyle::new(0.5, GRAY),
background_fill: Rgba::luminance_alpha(0.013, 0.95).into(),
dark_bg_color: Srgba::black_alpha(140),
cursor_blink_hz: None, // Some(1.0)
text_cursor_width: 2.0,
animation_time: 1.0 / 15.0,
window: Window::default(),
menu_bar: MenuBar::default(),
clip_rect_margin: 3.0,
debug_widget_rects: false,
debug_resize: false,
}
}
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Interact { pub struct Interacted {
pub active: WidgetStyle, pub active: WidgetVisuals,
pub hovered: WidgetStyle, pub hovered: WidgetVisuals,
pub inactive: WidgetStyle, pub inactive: WidgetVisuals,
pub disabled: WidgetStyle, pub disabled: WidgetVisuals,
} }
impl Default for Interact { impl Interacted {
fn default() -> Self { pub fn style(&self, response: &Response) -> &WidgetVisuals {
Self {
active: WidgetStyle {
bg_fill: Some(Srgba::black_alpha(128)),
bg_outline: Some(LineStyle::new(2.0, WHITE)),
corner_radius: 0.0,
main_fill: srgba(120, 120, 200, 255),
stroke_color: WHITE,
stroke_width: 2.0,
},
hovered: WidgetStyle {
bg_fill: None,
bg_outline: Some(LineStyle::new(1.0, WHITE)),
corner_radius: 2.0,
main_fill: srgba(100, 100, 150, 255),
stroke_color: Srgba::gray(240),
stroke_width: 1.5,
},
inactive: WidgetStyle {
bg_fill: None,
bg_outline: Some(LineStyle::new(1.0, Srgba::gray(128))),
corner_radius: 4.0,
main_fill: srgba(60, 60, 80, 255),
stroke_color: Srgba::gray(200), // Mustn't look grayed out!
stroke_width: 1.0,
},
disabled: WidgetStyle {
bg_fill: None,
bg_outline: Some(LineStyle::new(0.5, Srgba::gray(128))),
corner_radius: 4.0,
main_fill: srgba(50, 50, 50, 255),
stroke_color: Srgba::gray(128), // Should look grayed out
stroke_width: 0.5,
},
}
}
}
impl Interact {
pub fn style(&self, response: &Response) -> &WidgetStyle {
if response.active || response.has_kb_focus { if response.active || response.has_kb_focus {
&self.active &self.active
} else if response.sense == Sense::nothing() { } else if response.sense == Sense::nothing() {
@ -167,7 +137,7 @@ impl Interact {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct WidgetStyle { pub struct WidgetVisuals {
/// Background color of widget /// Background color of widget
pub bg_fill: Option<Srgba>, pub bg_fill: Option<Srgba>,
@ -189,82 +159,306 @@ pub struct WidgetStyle {
pub stroke_width: f32, pub stroke_width: f32,
} }
impl WidgetStyle { impl WidgetVisuals {
pub fn line_style(&self) -> LineStyle { pub fn line_style(&self) -> LineStyle {
LineStyle::new(self.stroke_width, self.stroke_color) LineStyle::new(self.stroke_width, self.stroke_color)
} }
} }
#[derive(Clone, Copy, Debug)] // ----------------------------------------------------------------------------
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Window {
pub corner_radius: f32,
}
impl Default for Window { impl Default for Style {
fn default() -> Self { fn default() -> Self {
Self { Self {
corner_radius: 10.0, spacing: Spacing::default(),
interaction: Interaction::default(),
visuals: Visuals::default(),
animation_time: 1.0 / 15.0,
} }
} }
} }
#[derive(Clone, Copy, Debug)] impl Default for Spacing {
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct MenuBar {
pub height: f32,
}
impl Default for MenuBar {
fn default() -> Self { fn default() -> Self {
Self { height: 16.0 } Self {
item_spacing: vec2(8.0, 4.0),
window_padding: vec2(6.0, 6.0),
button_padding: vec2(4.0, 1.0),
indent: 21.0,
clickable_diameter: 22.0,
slider_width: 140.0,
icon_width: 14.0,
menu_bar_height: 16.0,
}
} }
} }
impl Style { impl Default for Interaction {
// TODO: rename style.interact() to something better fn default() -> Self {
/// Use this style for interactive things Self {
pub fn interact(&self, response: &Response) -> &WidgetStyle { resize_grab_radius_side: 5.0,
self.interact.style(response) resize_grab_radius_corner: 10.0,
} }
/// Returns small icon rectangle and big icon rectangle
pub fn icon_rectangles(&self, rect: Rect) -> (Rect, Rect) {
let box_side = self.start_icon_width;
let big_icon_rect = Rect::from_center_size(
pos2(rect.left() + box_side / 2.0, rect.center().y),
vec2(box_side, box_side),
);
let small_rect_side = 8.0; // TODO: make a parameter
let small_icon_rect =
Rect::from_center_size(big_icon_rect.center(), Vec2::splat(small_rect_side));
(small_icon_rect, big_icon_rect)
} }
} }
impl Default for Visuals {
fn default() -> Self {
Self {
interacted: Default::default(),
text_color: Srgba::gray(160),
line_width: 1.0,
thin_outline: LineStyle::new(0.5, GRAY),
background_fill: Rgba::luminance_alpha(0.013, 0.95).into(),
dark_bg_color: Srgba::black_alpha(140),
window_corner_radius: 10.0,
resize_corner_size: 16.0,
cursor_blink_hz: None, // Some(1.0)
text_cursor_width: 2.0,
clip_rect_margin: 3.0,
debug_widget_rects: false,
debug_resize: false,
}
}
}
impl Default for Interacted {
fn default() -> Self {
Self {
active: WidgetVisuals {
bg_fill: Some(Srgba::black_alpha(128)),
bg_outline: Some(LineStyle::new(2.0, WHITE)),
corner_radius: 0.0,
main_fill: srgba(120, 120, 200, 255),
stroke_color: WHITE,
stroke_width: 2.0,
},
hovered: WidgetVisuals {
bg_fill: None,
bg_outline: Some(LineStyle::new(1.0, WHITE)),
corner_radius: 2.0,
main_fill: srgba(100, 100, 150, 255),
stroke_color: Srgba::gray(240),
stroke_width: 1.5,
},
inactive: WidgetVisuals {
bg_fill: None,
bg_outline: Some(LineStyle::new(1.0, Srgba::gray(128))),
corner_radius: 4.0,
main_fill: srgba(60, 60, 80, 255),
stroke_color: Srgba::gray(200), // Mustn't look grayed out!
stroke_width: 1.0,
},
disabled: WidgetVisuals {
bg_fill: None,
bg_outline: Some(LineStyle::new(0.5, Srgba::gray(128))),
corner_radius: 4.0,
main_fill: srgba(50, 50, 50, 255),
stroke_color: Srgba::gray(128), // Should look grayed out
stroke_width: 0.5,
},
}
}
}
// ----------------------------------------------------------------------------
use crate::{widgets::*, Ui};
impl Style { impl Style {
#[rustfmt::skip]
pub fn ui(&mut self, ui: &mut crate::Ui) { pub fn ui(&mut self, ui: &mut crate::Ui) {
use crate::{widgets::*}; if ui.add(Button::new("Reset")).clicked {
if ui.add(Button::new("Reset style")).clicked {
*self = Default::default(); *self = Default::default();
} }
ui.add(Checkbox::new(&mut self.debug_widget_rects, "Paint debug rectangles around widgets")); let Self {
ui.add(Checkbox::new(&mut self.debug_resize, "Debug Resize")); spacing,
interaction,
ui.add(Slider::f32(&mut self.item_spacing.x, 0.0..=10.0).text("item_spacing.x")); visuals,
ui.add(Slider::f32(&mut self.item_spacing.y, 0.0..=10.0).text("item_spacing.y")); animation_time,
ui.add(Slider::f32(&mut self.window_padding.x, 0.0..=10.0).text("window_padding.x")); } = self;
ui.add(Slider::f32(&mut self.window_padding.y, 0.0..=10.0).text("window_padding.y")); ui.collapsing("Spacing", |ui| spacing.ui(ui));
ui.add(Slider::f32(&mut self.indent, 0.0..=100.0).text("indent")); ui.collapsing("Interaction", |ui| interaction.ui(ui));
ui.add(Slider::f32(&mut self.button_padding.x, 0.0..=20.0).text("button_padding.x")); ui.collapsing("Visuals", |ui| visuals.ui(ui));
ui.add(Slider::f32(&mut self.button_padding.y, 0.0..=20.0).text("button_padding.y")); ui.add(Slider::f32(animation_time, 0.0..=1.0).text("animation_time"));
ui.add(Slider::f32(&mut self.clickable_diameter, 0.0..=60.0).text("clickable_diameter"));
ui.add(Slider::f32(&mut self.start_icon_width, 0.0..=60.0).text("start_icon_width"));
ui.add(Slider::f32(&mut self.line_width, 0.0..=10.0).text("line_width"));
ui.add(Slider::f32(&mut self.animation_time, 0.0..=1.0).text("animation_time"));
} }
} }
impl Spacing {
pub fn ui(&mut self, ui: &mut crate::Ui) {
if ui.add(Button::new("Reset")).clicked {
*self = Default::default();
}
let Self {
item_spacing,
window_padding,
button_padding,
indent,
clickable_diameter,
slider_width,
icon_width,
menu_bar_height,
} = self;
ui_slider_vec2(ui, item_spacing, 0.0..=20.0, "item_spacing");
ui_slider_vec2(ui, window_padding, 0.0..=20.0, "window_padding");
ui_slider_vec2(ui, button_padding, 0.0..=20.0, "button_padding");
ui.add(Slider::f32(indent, 0.0..=100.0).text("indent"));
ui.add(Slider::f32(clickable_diameter, 0.0..=40.0).text("clickable_diameter"));
ui.add(Slider::f32(slider_width, 0.0..=1000.0).text("slider_width"));
ui.add(Slider::f32(icon_width, 0.0..=40.0).text("icon_width"));
ui.add(Slider::f32(menu_bar_height, 0.0..=40.0).text("menu_bar_height"));
}
}
impl Interaction {
pub fn ui(&mut self, ui: &mut crate::Ui) {
if ui.add(Button::new("Reset")).clicked {
*self = Default::default();
}
let Self {
resize_grab_radius_side,
resize_grab_radius_corner,
} = self;
ui.add(Slider::f32(resize_grab_radius_side, 0.0..=20.0).text("resize_grab_radius_side"));
ui.add(
Slider::f32(resize_grab_radius_corner, 0.0..=20.0).text("resize_grab_radius_corner"),
);
}
}
impl Interacted {
pub fn ui(&mut self, ui: &mut crate::Ui) {
if ui.add(Button::new("Reset")).clicked {
*self = Default::default();
}
let Self {
active,
hovered,
inactive,
disabled,
} = self;
ui.collapsing("active", |ui| active.ui(ui));
ui.collapsing("hovered", |ui| hovered.ui(ui));
ui.collapsing("inactive", |ui| inactive.ui(ui));
ui.collapsing("disabled", |ui| disabled.ui(ui));
}
}
impl WidgetVisuals {
pub fn ui(&mut self, ui: &mut crate::Ui) {
let Self {
bg_fill,
bg_outline,
corner_radius,
main_fill,
stroke_color,
stroke_width,
} = self;
let _ = bg_fill; // ui_color(ui, bg_fill, "bg_fill"); // TODO
let _ = bg_outline; // bg_outline.ui(ui, "bg_outline");// TODO
ui.add(Slider::f32(corner_radius, 0.0..=10.0).text("corner_radius"));
ui_color(ui, main_fill, "main_fill");
ui_color(ui, stroke_color, "stroke_color");
ui.add(Slider::f32(stroke_width, 0.0..=10.0).text("stroke_width"));
}
}
impl Visuals {
pub fn ui(&mut self, ui: &mut crate::Ui) {
if ui.add(Button::new("Reset")).clicked {
*self = Default::default();
}
let Self {
interacted,
text_color,
line_width,
thin_outline,
background_fill,
dark_bg_color,
window_corner_radius,
resize_corner_size,
cursor_blink_hz,
text_cursor_width,
clip_rect_margin,
debug_widget_rects,
debug_resize,
} = self;
ui.collapsing("interacted", |ui| interacted.ui(ui));
ui_color(ui, text_color, "text_color");
ui.add(Slider::f32(line_width, 0.0..=10.0).text("line_width"));
thin_outline.ui(ui, "thin_outline");
ui_color(ui, background_fill, "background_fill");
ui_color(ui, dark_bg_color, "dark_bg_color");
ui.add(Slider::f32(window_corner_radius, 0.0..=20.0).text("window_corner_radius"));
ui.add(Slider::f32(resize_corner_size, 0.0..=20.0).text("resize_corner_size"));
let _ = cursor_blink_hz; // TODO
ui.add(Slider::f32(text_cursor_width, 0.0..=2.0).text("text_cursor_width"));
ui.add(Slider::f32(clip_rect_margin, 0.0..=20.0).text("clip_rect_margin"));
ui.add(Checkbox::new(
debug_widget_rects,
"Paint debug rectangles around widgets",
));
ui.add(Checkbox::new(debug_resize, "Debug Resize"));
}
}
impl LineStyle {
pub fn ui(&mut self, ui: &mut crate::Ui, text: &str) {
let Self { width, color } = self;
ui.horizontal_centered(|ui| {
ui.label(format!("{}: ", text));
ui.add(Slider::f32(width, 0.0..=10.0));
ui_color(ui, color, "color");
});
}
}
// TODO: improve and standardize ui_slider_vec2
fn ui_slider_vec2(ui: &mut Ui, value: &mut Vec2, range: std::ops::RangeInclusive<f32>, text: &str) {
ui.horizontal_centered(|ui| {
ui.label(format!("{}: ", text));
ui.add(Slider::f32(&mut value.x, range.clone()))
.tooltip_text("x");
ui.add(Slider::f32(&mut value.y, range)).tooltip_text("y");
});
}
// TODO: improve color picker
fn ui_color(ui: &mut Ui, srgba: &mut Srgba, text: &str) {
ui.horizontal_centered(|ui| {
// TODO: DragValue::u8
// ui.label(format!("{} sRGBA: ", text));
// ui.add(DragValue::u8(&mut srgba.r).speed(1))
// .tooltip_text("r");
// ui.add(DragValue::u8(&mut srgba.g).speed(1))
// .tooltip_text("g");
// ui.add(DragValue::u8(&mut srgba.b).speed(1))
// .tooltip_text("b");
// ui.add(DragValue::u8(&mut srgba.a).speed(1))
// .tooltip_text("a");
ui.label(format!("{} RGBA: ", text));
let mut rgba = Rgba::from(*srgba);
ui.add(DragValue::f32(&mut rgba.r).speed(0.003))
.tooltip_text("r");
ui.add(DragValue::f32(&mut rgba.g).speed(0.003))
.tooltip_text("g");
ui.add(DragValue::f32(&mut rgba.b).speed(0.003))
.tooltip_text("b");
ui.add(DragValue::f32(&mut rgba.a).speed(0.003))
.tooltip_text("a");
if rgba != Rgba::from(*srgba) {
*srgba = Srgba::from(rgba);
}
});
}

View file

@ -38,8 +38,8 @@ pub struct Ui {
/// Where the next widget will be put. /// Where the next widget will be put.
/// Progresses along self.dir. /// Progresses along self.dir.
/// Initially set to rect.min /// Initially set to rect.min
/// If something has already been added, this will point ot style.item_spacing beyond the latest child. /// If something has already been added, this will point ot style.spacing.item_spacing beyond the latest child.
/// The cursor can thus be style.item_spacing pixels outside of the child_bounds. /// The cursor can thus be style.spacing.item_spacing pixels outside of the child_bounds.
cursor: Pos2, // TODO: move into Layout? cursor: Pos2, // TODO: move into Layout?
/// How many children has been added to us? /// How many children has been added to us?
@ -54,7 +54,7 @@ impl Ui {
pub fn new(ctx: Arc<Context>, layer: Layer, id: Id, rect: Rect) -> Self { pub fn new(ctx: Arc<Context>, layer: Layer, id: Id, rect: Rect) -> Self {
let style = ctx.style(); let style = ctx.style();
let clip_rect = rect.expand(style.clip_rect_margin); let clip_rect = rect.expand(style.visuals.clip_rect_margin);
Ui { Ui {
id, id,
painter: Painter::new(ctx, layer, clip_rect), painter: Painter::new(ctx, layer, clip_rect),
@ -75,7 +75,7 @@ impl Ui {
painter: self.painter.clone(), painter: self.painter.clone(),
desired_rect: child_rect, desired_rect: child_rect,
child_bounds: Rect::from_min_size(child_rect.min, Vec2::zero()), // TODO: Rect::nothing() ? child_bounds: Rect::from_min_size(child_rect.min, Vec2::zero()), // TODO: Rect::nothing() ?
style: self.style.clone(), style: self.style().clone(),
layout: self.layout, layout: self.layout,
cursor: child_rect.min, cursor: child_rect.min,
child_count: 0, child_count: 0,
@ -366,7 +366,7 @@ impl Ui {
let rect = self.reserve_space_impl(desired_size); let rect = self.reserve_space_impl(desired_size);
if self.style().debug_widget_rects { if self.style().visuals.debug_widget_rects {
self.painter.rect_outline(rect, 0.0, (1.0, LIGHT_BLUE)); self.painter.rect_outline(rect, 0.0, (1.0, LIGHT_BLUE));
let color = color::srgba(200, 0, 0, 255); let color = color::srgba(200, 0, 0, 255);
@ -535,7 +535,7 @@ impl Ui {
self.layout().dir() == Direction::Vertical, self.layout().dir() == Direction::Vertical,
"You can only indent vertical layouts" "You can only indent vertical layouts"
); );
let indent = vec2(self.style.indent, 0.0); let indent = vec2(self.style().spacing.indent, 0.0);
let child_rect = Rect::from_min_max(self.cursor + indent, self.bottom_right()); let child_rect = Rect::from_min_max(self.cursor + indent, self.bottom_right());
let mut child_ui = Ui { let mut child_ui = Ui {
id: self.id.with(id_source), id: self.id.with(id_source),
@ -550,7 +550,7 @@ impl Ui {
let line_end = pos2(line_start.x, line_start.y + size.y - 2.0); let line_end = pos2(line_start.x, line_start.y + size.y - 2.0);
self.painter.line_segment( self.painter.line_segment(
[line_start, line_end], [line_start, line_end],
(self.style.line_width, Srgba::gray(150)), (self.style().visuals.line_width, Srgba::gray(150)),
); );
(ret, self.allocate_space(indent + size)) (ret, self.allocate_space(indent + size))
@ -631,7 +631,7 @@ impl Ui {
F: FnOnce(&mut [Self]) -> R, F: FnOnce(&mut [Self]) -> R,
{ {
// TODO: ensure there is space // TODO: ensure there is space
let spacing = self.style.item_spacing.x; let spacing = self.style().spacing.item_spacing.x;
let total_spacing = spacing * (num_columns as f32 - 1.0); let total_spacing = spacing * (num_columns as f32 - 1.0);
let column_width = (self.available().width() - total_spacing) / (num_columns as f32); let column_width = (self.available().width() - total_spacing) / (num_columns as f32);

View file

@ -107,7 +107,9 @@ impl Label {
// This should be the easiest method of putting text anywhere. // This should be the easiest method of putting text anywhere.
pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: font::Galley) { pub fn paint_galley(&self, ui: &mut Ui, pos: Pos2, galley: font::Galley) {
let text_color = self.text_color.unwrap_or_else(|| ui.style().text_color); let text_color = self
.text_color
.unwrap_or_else(|| ui.style().visuals.text_color);
ui.painter() ui.painter()
.galley(pos, galley, self.text_style, text_color); .galley(pos, galley, self.text_style, text_color);
} }
@ -201,7 +203,7 @@ impl Widget for Hyperlink {
let max_x = pos.x + line.max_x(); let max_x = pos.x + line.max_x();
ui.painter().line_segment( ui.painter().line_segment(
[pos2(min_x, y), pos2(max_x, y)], [pos2(min_x, y), pos2(max_x, y)],
(ui.style().line_width, color), (ui.style().visuals.line_width, color),
); );
} }
} }
@ -281,9 +283,9 @@ impl Widget for Button {
let id = ui.make_position_id(); let id = ui.make_position_id();
let font = &ui.fonts()[text_style]; let font = &ui.fonts()[text_style];
let galley = font.layout_multiline(text, ui.available().width()); let galley = font.layout_multiline(text, ui.available().width());
let padding = ui.style().button_padding; let padding = ui.style().spacing.button_padding;
let mut size = galley.size + 2.0 * padding; let mut size = galley.size + 2.0 * padding;
size.y = size.y.max(ui.style().clickable_diameter); size.y = size.y.max(ui.style().spacing.clickable_diameter);
let rect = ui.allocate_space(size); let rect = ui.allocate_space(size);
let response = ui.interact(rect, id, sense); let response = ui.interact(rect, id, sense);
let text_cursor = response.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y); let text_cursor = response.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
@ -340,18 +342,19 @@ impl<'a> Widget for Checkbox<'a> {
let text_style = TextStyle::Button; let text_style = TextStyle::Button;
let font = &ui.fonts()[text_style]; let font = &ui.fonts()[text_style];
let galley = font.layout_single_line(text); let galley = font.layout_single_line(text);
let size = ui.style().button_padding let size = ui.style().spacing.button_padding
+ vec2(ui.style().start_icon_width, 0.0) + vec2(ui.style().spacing.icon_width, 0.0)
+ galley.size + galley.size
+ ui.style().button_padding; + ui.style().spacing.button_padding;
let rect = ui.allocate_space(size); let rect = ui.allocate_space(size);
let response = ui.interact(rect, id, Sense::click()); let response = ui.interact(rect, id, Sense::click());
let text_cursor = let text_cursor = response.rect.min
response.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0); + ui.style().spacing.button_padding
+ vec2(ui.style().spacing.icon_width, 0.0);
if response.clicked { if response.clicked {
*checked = !*checked; *checked = !*checked;
} }
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(response.rect); let (small_icon_rect, big_icon_rect) = ui.style().spacing.icon_rectangles(response.rect);
ui.painter().add(PaintCmd::Rect { ui.painter().add(PaintCmd::Rect {
rect: big_icon_rect, rect: big_icon_rect,
corner_radius: ui.style().interact(&response).corner_radius, corner_radius: ui.style().interact(&response).corner_radius,
@ -369,7 +372,7 @@ impl<'a> Widget for Checkbox<'a> {
pos2(small_icon_rect.right(), small_icon_rect.top()), pos2(small_icon_rect.right(), small_icon_rect.top()),
]), ]),
closed: false, closed: false,
outline: Some(LineStyle::new(ui.style().line_width, stroke_color)), outline: Some(LineStyle::new(ui.style().visuals.line_width, stroke_color)),
fill: None, fill: None,
}); });
} }
@ -417,19 +420,20 @@ impl Widget for RadioButton {
let text_style = TextStyle::Button; let text_style = TextStyle::Button;
let font = &ui.fonts()[text_style]; let font = &ui.fonts()[text_style];
let galley = font.layout_multiline(text, ui.available().width()); let galley = font.layout_multiline(text, ui.available().width());
let size = ui.style().button_padding let size = ui.style().spacing.button_padding
+ vec2(ui.style().start_icon_width, 0.0) + vec2(ui.style().spacing.icon_width, 0.0)
+ galley.size + galley.size
+ ui.style().button_padding; + ui.style().spacing.button_padding;
let rect = ui.allocate_space(size); let rect = ui.allocate_space(size);
let response = ui.interact(rect, id, Sense::click()); let response = ui.interact(rect, id, Sense::click());
let text_cursor = let text_cursor = response.rect.min
response.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0); + ui.style().spacing.button_padding
+ vec2(ui.style().spacing.icon_width, 0.0);
let bg_fill = ui.style().interact(&response).bg_fill; let bg_fill = ui.style().interact(&response).bg_fill;
let stroke_color = ui.style().interact(&response).stroke_color; let stroke_color = ui.style().interact(&response).stroke_color;
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(response.rect); let (small_icon_rect, big_icon_rect) = ui.style().spacing.icon_rectangles(response.rect);
let painter = ui.painter(); let painter = ui.painter();
@ -507,7 +511,7 @@ impl Widget for Separator {
color, color,
} = self; } = self;
let line_width = line_width.unwrap_or_else(|| ui.style().line_width); let line_width = line_width.unwrap_or_else(|| ui.style().visuals.line_width);
let available_space = ui.available_finite().size(); let available_space = ui.available_finite().size();

View file

@ -126,7 +126,7 @@ impl<'a> Slider<'a> {
/// Just the slider, no text /// Just the slider, no text
fn allocate_slide_space(&self, ui: &mut Ui, height: f32) -> Response { fn allocate_slide_space(&self, ui: &mut Ui, height: f32) -> Response {
let id = self.id.unwrap_or_else(|| ui.make_position_id()); let id = self.id.unwrap_or_else(|| ui.make_position_id());
let desired_size = vec2(ui.available().width(), height); let desired_size = vec2(ui.style().spacing.slider_width, height);
let rect = ui.allocate_space(desired_size); let rect = ui.allocate_space(desired_size);
ui.interact(rect, id, Sense::click_and_drag()) ui.interact(rect, id, Sense::click_and_drag())
} }
@ -164,7 +164,7 @@ impl<'a> Slider<'a> {
ui.painter().add(PaintCmd::Rect { ui.painter().add(PaintCmd::Rect {
rect: rail_rect, rect: rail_rect,
corner_radius: rail_radius, corner_radius: rail_radius,
fill: Some(ui.style().background_fill), fill: Some(ui.style().visuals.background_fill),
outline: Some(LineStyle::new(1.0, Srgba::gray(200))), // TODO outline: Some(LineStyle::new(1.0, Srgba::gray(200))), // TODO
}); });
@ -182,10 +182,12 @@ impl<'a> Slider<'a> {
/// Just the text label /// Just the text label
fn text_ui(&mut self, ui: &mut Ui, x_range: RangeInclusive<f32>) { fn text_ui(&mut self, ui: &mut Ui, x_range: RangeInclusive<f32>) {
let text_color = self.text_color.unwrap_or_else(|| ui.style().text_color); let text_color = self
.text_color
.unwrap_or_else(|| ui.style().visuals.text_color);
if let Some(label_text) = self.text.as_deref() { if let Some(label_text) = self.text.as_deref() {
ui.style_mut().item_spacing.x = 0.0; ui.style_mut().spacing.item_spacing.x = 0.0;
ui.add( ui.add(
Label::new(format!("{}: ", label_text)) Label::new(format!("{}: ", label_text))
.multiline(false) .multiline(false)
@ -258,30 +260,21 @@ impl<'a> Widget for Slider<'a> {
fn ui(mut self, ui: &mut Ui) -> Response { fn ui(mut self, ui: &mut Ui) -> Response {
let text_style = TextStyle::Button; let text_style = TextStyle::Button;
let font = &ui.fonts()[text_style]; let font = &ui.fonts()[text_style];
let height = font.line_spacing().max(ui.style().clickable_diameter); let height = font
.line_spacing()
.max(ui.style().spacing.clickable_diameter);
if let Some(text) = &self.text { if let Some(text) = &self.text {
self.id = self.id.or_else(|| Some(ui.make_unique_child_id(text))); self.id = self.id.or_else(|| Some(ui.make_unique_child_id(text)));
ui.columns(2, |columns| { ui.horizontal_centered(|ui| {
let slider_ui = &mut columns[0]; let slider_response = self.allocate_slide_space(ui, height);
let slider_response = self.allocate_slide_space(slider_ui, height); self.slider_ui(ui, &slider_response);
self.slider_ui(slider_ui, &slider_response);
let x_range = x_range(&slider_response.rect); let x_range = x_range(&slider_response.rect);
// Place the text in line with the slider on the left:
let text_ui = &mut columns[1];
text_ui.set_desired_height(slider_response.rect.height());
text_ui.inner_layout(
Layout::horizontal(Align::Center),
text_ui.available().size(),
|ui| {
self.text_ui(ui, x_range); self.text_ui(ui, x_range);
},
);
slider_response slider_response
}) })
.0
} else { } else {
let response = self.allocate_slide_space(ui, height); let response = self.allocate_slide_space(ui, height);
self.slider_ui(ui, &response); self.slider_ui(ui, &response);

View file

@ -183,13 +183,13 @@ impl<'t> Widget for TextEdit<'t> {
painter.add(PaintCmd::Rect { painter.add(PaintCmd::Rect {
rect: bg_rect, rect: bg_rect,
corner_radius: ui.style().interact(&response).corner_radius, corner_radius: ui.style().interact(&response).corner_radius,
fill: Some(ui.style().dark_bg_color), fill: Some(ui.style().visuals.dark_bg_color),
outline: ui.style().interact(&response).bg_outline, outline: ui.style().interact(&response).bg_outline,
}); });
} }
if ui.memory().has_kb_focus(id) { if ui.memory().has_kb_focus(id) {
let cursor_blink_hz = ui.style().cursor_blink_hz; let cursor_blink_hz = ui.style().visuals.cursor_blink_hz;
let show_cursor = if let Some(cursor_blink_hz) = cursor_blink_hz { let show_cursor = if let Some(cursor_blink_hz) = cursor_blink_hz {
ui.ctx().request_repaint(); // TODO: only when cursor blinks on or off ui.ctx().request_repaint(); // TODO: only when cursor blinks on or off
(ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0 (ui.input().time * cursor_blink_hz as f64 * 3.0).floor() as i64 % 3 != 0
@ -202,7 +202,7 @@ impl<'t> Widget for TextEdit<'t> {
let cursor_pos = response.rect.min + galley.char_start_pos(cursor); let cursor_pos = response.rect.min + galley.char_start_pos(cursor);
painter.line_segment( painter.line_segment(
[cursor_pos, cursor_pos + vec2(0.0, line_spacing)], [cursor_pos, cursor_pos + vec2(0.0, line_spacing)],
(ui.style().text_cursor_width, color::WHITE), (ui.style().visuals.text_cursor_width, color::WHITE),
); );
} }
} }