Simplify and improve the default visual style
This commit is contained in:
parent
01568acef2
commit
037b22be7f
17 changed files with 393 additions and 189 deletions
|
@ -53,6 +53,7 @@ pub const fn additive_gray(l: u8) -> Color {
|
|||
pub const TRANSPARENT: Color = srgba(0, 0, 0, 0);
|
||||
pub const BLACK: Color = srgba(0, 0, 0, 255);
|
||||
pub const LIGHT_GRAY: Color = srgba(220, 220, 220, 255);
|
||||
pub const GRAY: Color = srgba(160, 160, 160, 255);
|
||||
pub const WHITE: Color = srgba(255, 255, 255, 255);
|
||||
pub const RED: Color = srgba(255, 0, 0, 255);
|
||||
pub const GREEN: Color = srgba(0, 255, 0, 255);
|
||||
|
|
|
@ -63,7 +63,8 @@ impl CollapsingHeader {
|
|||
let text_pos = available.min + vec2(ui.style().indent, 0.0);
|
||||
let galley = label.layout(available.width() - ui.style().indent, ui);
|
||||
let text_max_x = text_pos.x + galley.size.x;
|
||||
let desired_width = available.width().max(text_max_x - available.left());
|
||||
let desired_width = text_max_x - available.left();
|
||||
let desired_width = desired_width.max(available.width());
|
||||
|
||||
let interact = ui.reserve_space(
|
||||
vec2(
|
||||
|
@ -87,9 +88,19 @@ impl CollapsingHeader {
|
|||
*state
|
||||
};
|
||||
|
||||
let animation_time = ui.style().animation_time;
|
||||
let time_since_toggle = (ui.input().time - state.toggle_time) as f32;
|
||||
let time_since_toggle = time_since_toggle + ui.input().dt; // Instant feedback
|
||||
let openness = if state.open {
|
||||
remap_clamp(time_since_toggle, 0.0..=animation_time, 0.0..=1.0)
|
||||
} else {
|
||||
remap_clamp(time_since_toggle, 0.0..=animation_time, 1.0..=0.0)
|
||||
};
|
||||
let animate = time_since_toggle < animation_time;
|
||||
|
||||
let where_to_put_background = ui.paint_list_len();
|
||||
|
||||
paint_icon(ui, &state, &interact);
|
||||
paint_icon(ui, &interact, openness);
|
||||
|
||||
ui.add_galley(
|
||||
text_pos,
|
||||
|
@ -102,18 +113,14 @@ impl CollapsingHeader {
|
|||
where_to_put_background,
|
||||
PaintCmd::Rect {
|
||||
corner_radius: ui.style().interact(&interact).corner_radius,
|
||||
fill_color: ui.style().interact(&interact).fill_color,
|
||||
outline: ui.style().interact(&interact).outline,
|
||||
fill_color: ui.style().interact(&interact).bg_fill_color,
|
||||
outline: None,
|
||||
rect: interact.rect,
|
||||
},
|
||||
);
|
||||
|
||||
ui.expand_to_include_child(interact.rect); // TODO: remove, just a test
|
||||
|
||||
let animation_time = ui.style().animation_time;
|
||||
let time_since_toggle = (ui.input().time - state.toggle_time) as f32;
|
||||
let time_since_toggle = time_since_toggle + ui.input().dt; // Instant feedback
|
||||
let animate = time_since_toggle < animation_time;
|
||||
if animate {
|
||||
ui.indent(id, |child_ui| {
|
||||
let max_height = if state.open {
|
||||
|
@ -154,7 +161,7 @@ impl CollapsingHeader {
|
|||
}
|
||||
}
|
||||
|
||||
fn paint_icon(ui: &mut Ui, state: &State, interact: &InteractInfo) {
|
||||
fn paint_icon(ui: &mut Ui, interact: &InteractInfo, openness: f32) {
|
||||
let stroke_color = ui.style().interact(interact).stroke_color;
|
||||
let stroke_width = ui.style().interact(interact).stroke_width;
|
||||
|
||||
|
@ -164,25 +171,24 @@ fn paint_icon(ui: &mut Ui, state: &State, interact: &InteractInfo) {
|
|||
interact.rect.center().y,
|
||||
));
|
||||
|
||||
// Draw a minus:
|
||||
ui.add_paint_cmd(PaintCmd::LineSegment {
|
||||
points: [
|
||||
pos2(small_icon_rect.left(), small_icon_rect.center().y),
|
||||
pos2(small_icon_rect.right(), small_icon_rect.center().y),
|
||||
],
|
||||
color: stroke_color,
|
||||
width: stroke_width,
|
||||
});
|
||||
|
||||
if !state.open {
|
||||
// Draw it as a plus:
|
||||
ui.add_paint_cmd(PaintCmd::LineSegment {
|
||||
points: [
|
||||
pos2(small_icon_rect.center().x, small_icon_rect.top()),
|
||||
pos2(small_icon_rect.center().x, small_icon_rect.bottom()),
|
||||
],
|
||||
color: stroke_color,
|
||||
width: stroke_width,
|
||||
});
|
||||
// Draw a pointy triangle arrow:
|
||||
let rect = Rect::from_center_size(
|
||||
small_icon_rect.center(),
|
||||
vec2(small_icon_rect.width(), small_icon_rect.height()) * 0.75,
|
||||
);
|
||||
let mut points = [rect.left_top(), rect.right_top(), rect.center_bottom()];
|
||||
let rotation = Vec2::angled(remap(openness, 0.0..=1.0, -TAU / 4.0..=0.0));
|
||||
for p in &mut points {
|
||||
let v = *p - rect.center();
|
||||
let v = rotation.rotate_other(v);
|
||||
*p = rect.center() + v;
|
||||
}
|
||||
// }
|
||||
|
||||
ui.add_paint_cmd(PaintCmd::Path {
|
||||
path: mesher::Path::from_point_loop(&points),
|
||||
closed: true,
|
||||
fill_color: None,
|
||||
outline: Some(Outline::new(stroke_width, stroke_color)),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,16 +15,25 @@ impl Frame {
|
|||
Self {
|
||||
margin: style.window_padding,
|
||||
corner_radius: style.window.corner_radius,
|
||||
fill_color: Some(style.background_fill_color()),
|
||||
fill_color: Some(style.background_fill_color),
|
||||
outline: Some(Outline::new(1.0, color::WHITE)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn menu_bar(_style: &Style) -> Self {
|
||||
Self {
|
||||
margin: Vec2::splat(1.0),
|
||||
corner_radius: 0.0,
|
||||
fill_color: None,
|
||||
outline: Some(Outline::new(0.5, color::white(128))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn menu(style: &Style) -> Self {
|
||||
Self {
|
||||
margin: Vec2::splat(1.0),
|
||||
corner_radius: 2.0,
|
||||
fill_color: Some(style.background_fill_color()),
|
||||
fill_color: Some(style.background_fill_color),
|
||||
outline: Some(Outline::new(1.0, color::white(128))),
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +42,7 @@ impl Frame {
|
|||
Self {
|
||||
margin: style.window_padding,
|
||||
corner_radius: 5.0,
|
||||
fill_color: Some(style.background_fill_color()),
|
||||
fill_color: Some(style.background_fill_color),
|
||||
outline: Some(Outline::new(1.0, color::white(128))),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,13 +21,16 @@ impl Default for BarState {
|
|||
}
|
||||
|
||||
pub fn bar(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) -> InteractInfo {
|
||||
ui.horizontal(|ui| {
|
||||
Frame::default().show(ui, |ui| {
|
||||
ui.inner_layout(Layout::horizontal(Align::Center), |ui| {
|
||||
Frame::menu_bar(ui.style()).show(ui, |ui| {
|
||||
let mut style = ui.style().clone();
|
||||
style.button_padding = vec2(2.0, 0.0);
|
||||
style.interact.inactive.fill_color = None;
|
||||
style.interact.inactive.outline = None;
|
||||
style.interact.hovered.fill_color = None;
|
||||
// style.interact.active.bg_fill_color = None;
|
||||
style.interact.active.rect_outline = None;
|
||||
// style.interact.hovered.bg_fill_color = None;
|
||||
style.interact.hovered.rect_outline = None;
|
||||
style.interact.inactive.bg_fill_color = None;
|
||||
style.interact.inactive.rect_outline = None;
|
||||
ui.set_style(style);
|
||||
|
||||
// Take full width and fixed height:
|
||||
|
@ -55,7 +58,7 @@ pub fn menu(ui: &mut Ui, title: impl Into<String>, add_contents: impl FnOnce(&mu
|
|||
let mut button = Button::new(title);
|
||||
|
||||
if bar_state.open_menu == Some(menu_id) {
|
||||
button = button.fill_color(ui.style().interact.active.fill_color);
|
||||
button = button.fill_color(Some(ui.style().interact.active.fill_color));
|
||||
}
|
||||
|
||||
let button_interact = ui.add(button);
|
||||
|
@ -75,11 +78,12 @@ pub fn menu(ui: &mut Ui, title: impl Into<String>, add_contents: impl FnOnce(&mu
|
|||
resize.show(ui, |ui| {
|
||||
let mut style = ui.style().clone();
|
||||
style.button_padding = vec2(2.0, 0.0);
|
||||
style.interact.inactive.fill_color = None;
|
||||
style.interact.inactive.outline = None;
|
||||
style.interact.active.corner_radius = 0.0;
|
||||
style.interact.hovered.corner_radius = 0.0;
|
||||
style.interact.inactive.corner_radius = 0.0;
|
||||
// style.interact.active.bg_fill_color = None;
|
||||
style.interact.active.rect_outline = None;
|
||||
// style.interact.hovered.bg_fill_color = None;
|
||||
style.interact.hovered.rect_outline = None;
|
||||
style.interact.inactive.bg_fill_color = None;
|
||||
style.interact.inactive.rect_outline = None;
|
||||
ui.set_style(style);
|
||||
ui.set_layout(Layout::justified(Direction::Vertical));
|
||||
add_contents(ui)
|
||||
|
|
|
@ -27,6 +27,7 @@ pub struct Resize {
|
|||
expand_width_to_fit_content: bool,
|
||||
expand_height_to_fit_content: bool,
|
||||
|
||||
outline: bool,
|
||||
handle_offset: Vec2,
|
||||
}
|
||||
|
||||
|
@ -34,13 +35,14 @@ impl Default for Resize {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
resizable: true,
|
||||
min_size: Vec2::splat(32.0),
|
||||
min_size: Vec2::splat(16.0),
|
||||
max_size: Vec2::infinity(),
|
||||
default_size: vec2(f32::INFINITY, 200.0), // TODO
|
||||
auto_shrink_width: false,
|
||||
auto_shrink_height: false,
|
||||
expand_width_to_fit_content: true,
|
||||
expand_height_to_fit_content: true,
|
||||
outline: true,
|
||||
handle_offset: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -128,12 +130,6 @@ impl Resize {
|
|||
self
|
||||
}
|
||||
|
||||
/// Offset the position of the resize handle by this much
|
||||
pub fn handle_offset(mut self, handle_offset: Vec2) -> Self {
|
||||
self.handle_offset = handle_offset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn auto_shrink_width(mut self, auto_shrink_width: bool) -> Self {
|
||||
self.auto_shrink_width = auto_shrink_width;
|
||||
self
|
||||
|
@ -143,6 +139,17 @@ impl Resize {
|
|||
self.auto_shrink_height = auto_shrink_height;
|
||||
self
|
||||
}
|
||||
|
||||
/// Offset the position of the resize handle by this much
|
||||
pub fn handle_offset(mut self, handle_offset: Vec2) -> Self {
|
||||
self.handle_offset = handle_offset;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn outline(mut self, outline: bool) -> Self {
|
||||
self.outline = outline;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: a common trait for Things that follow this pattern
|
||||
|
@ -240,6 +247,17 @@ impl Resize {
|
|||
|
||||
// ------------------------------
|
||||
|
||||
if self.outline && corner_interact.is_some() {
|
||||
let rect = Rect::from_min_size(position, state.size);
|
||||
let rect = rect.expand(2.0); // breathing room for content
|
||||
ui.add_paint_cmd(PaintCmd::Rect {
|
||||
rect,
|
||||
corner_radius: 3.0,
|
||||
fill_color: None,
|
||||
outline: Some(ui.style().thin_outline),
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(corner_interact) = corner_interact {
|
||||
paint_resize_corner(ui, &corner_interact);
|
||||
|
||||
|
|
|
@ -165,19 +165,19 @@ impl ScrollArea {
|
|||
|
||||
let style = outer_ui.style();
|
||||
let handle_fill_color = style.interact(&handle_interact).fill_color;
|
||||
let handle_outline = style.interact(&handle_interact).outline;
|
||||
let handle_outline = style.interact(&handle_interact).rect_outline;
|
||||
|
||||
outer_ui.add_paint_cmd(PaintCmd::Rect {
|
||||
rect: outer_scroll_rect,
|
||||
corner_radius,
|
||||
fill_color: Some(color::gray(0, 196)), // TODO style
|
||||
fill_color: Some(outer_ui.style().dark_bg_color),
|
||||
outline: None,
|
||||
});
|
||||
|
||||
outer_ui.add_paint_cmd(PaintCmd::Rect {
|
||||
rect: handle_rect.expand(-2.0),
|
||||
corner_radius,
|
||||
fill_color: handle_fill_color,
|
||||
fill_color: Some(handle_fill_color),
|
||||
outline: handle_outline,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -28,11 +28,12 @@ impl<'open> Window<'open> {
|
|||
area,
|
||||
frame: None,
|
||||
resize: Resize::default()
|
||||
.handle_offset(Vec2::splat(4.0))
|
||||
.auto_shrink_width(true)
|
||||
.auto_expand_height(false)
|
||||
.auto_expand_width(true)
|
||||
.auto_shrink_height(false)
|
||||
.auto_expand_height(false),
|
||||
.auto_shrink_width(true)
|
||||
.handle_offset(Vec2::splat(4.0))
|
||||
.outline(false),
|
||||
scroll: Some(
|
||||
ScrollArea::default()
|
||||
.always_show_scroll(false)
|
||||
|
@ -137,28 +138,89 @@ impl<'open> Window<'open> {
|
|||
|
||||
let frame = frame.unwrap_or_else(|| Frame::window(&ctx.style()));
|
||||
|
||||
// TODO: easier way to compose these
|
||||
area.show(ctx, |ui| {
|
||||
frame.show(ui, |ui| {
|
||||
resize.show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
// TODO: prettier close button, and to the right of the window
|
||||
if let Some(open) = open {
|
||||
if ui.add(Button::new("X")).clicked {
|
||||
*open = false;
|
||||
}
|
||||
if true {
|
||||
// TODO: easier way to compose these
|
||||
area.show(ctx, |ui| {
|
||||
frame.show(ui, |ui| {
|
||||
resize.show(ui, |ui| {
|
||||
show_title_bar(ui, title_label, open);
|
||||
if let Some(scroll) = scroll {
|
||||
scroll.show(ui, add_contents)
|
||||
} else {
|
||||
add_contents(ui)
|
||||
}
|
||||
ui.add(title_label);
|
||||
});
|
||||
ui.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents
|
||||
|
||||
if let Some(scroll) = scroll {
|
||||
scroll.show(ui, add_contents)
|
||||
} else {
|
||||
add_contents(ui)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// TODO: something like this, with collapsing contents
|
||||
area.show(ctx, |ui| {
|
||||
frame.show(ui, |ui| {
|
||||
CollapsingHeader::new(title_label.text()).show(ui, |ui| {
|
||||
resize.show(ui, |ui| {
|
||||
if let Some(scroll) = scroll {
|
||||
scroll.show(ui, add_contents)
|
||||
} else {
|
||||
add_contents(ui)
|
||||
}
|
||||
})
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_title_bar(ui: &mut Ui, title_label: Label, open: Option<&mut bool>) {
|
||||
let button_size = ui.style().clickable_diameter;
|
||||
|
||||
// TODO: show collapse button
|
||||
|
||||
let title_rect = ui.add(title_label).rect;
|
||||
|
||||
if let Some(open) = open {
|
||||
let close_max_x = title_rect.right() + ui.style().item_spacing.x + button_size;
|
||||
let close_max_x = close_max_x.max(ui.rect_finite().right());
|
||||
let close_rect = Rect::from_min_size(
|
||||
pos2(
|
||||
close_max_x - button_size,
|
||||
title_rect.center().y - 0.5 * button_size,
|
||||
),
|
||||
Vec2::splat(button_size),
|
||||
);
|
||||
if close_button(ui, close_rect).clicked {
|
||||
*open = false;
|
||||
}
|
||||
}
|
||||
|
||||
ui.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents
|
||||
}
|
||||
|
||||
fn close_button(ui: &mut Ui, rect: Rect) -> InteractInfo {
|
||||
let close_id = ui.make_child_id("window_close_button");
|
||||
let interact = ui.interact_rect(rect, close_id);
|
||||
ui.expand_to_include_child(interact.rect);
|
||||
|
||||
// ui.add_paint_cmd(PaintCmd::Rect {
|
||||
// corner_radius: ui.style().interact(&interact).corner_radius,
|
||||
// fill_color: ui.style().interact(&interact).bg_fill_color,
|
||||
// outline: ui.style().interact(&interact).rect_outline,
|
||||
// rect: interact.rect,
|
||||
// });
|
||||
|
||||
let rect = rect.expand(-4.0);
|
||||
|
||||
let stroke_color = ui.style().interact(&interact).stroke_color;
|
||||
let stroke_width = ui.style().interact(&interact).stroke_width;
|
||||
ui.add_paint_cmd(PaintCmd::line_segment(
|
||||
[rect.left_top(), rect.right_bottom()],
|
||||
stroke_color,
|
||||
stroke_width,
|
||||
));
|
||||
ui.add_paint_cmd(PaintCmd::line_segment(
|
||||
[rect.right_top(), rect.left_bottom()],
|
||||
stroke_color,
|
||||
stroke_width,
|
||||
));
|
||||
interact
|
||||
}
|
||||
|
|
|
@ -196,6 +196,7 @@ impl Context {
|
|||
fn paint(&self) -> PaintBatches {
|
||||
let mut mesher_options = *self.mesher_options.lock();
|
||||
mesher_options.aa_size = 1.0 / self.pixels_per_point();
|
||||
mesher_options.aa_size *= 1.5; // Looks better, but TODO: should not be needed
|
||||
let paint_commands = self.drain_paint_lists();
|
||||
let num_primitives = paint_commands.len();
|
||||
let batches = mesher::mesh_paint_commands(mesher_options, self.fonts(), paint_commands);
|
||||
|
@ -429,7 +430,7 @@ impl Context {
|
|||
text_style: TextStyle,
|
||||
color: Option<Color>,
|
||||
) {
|
||||
let color = color.unwrap_or_else(|| self.style().text_color());
|
||||
let color = color.unwrap_or_else(|| self.style().text_color);
|
||||
self.add_paint_cmd(
|
||||
layer,
|
||||
PaintCmd::Text {
|
||||
|
|
|
@ -18,7 +18,6 @@ pub struct ExampleApp {
|
|||
impl ExampleApp {
|
||||
pub fn ui(&mut self, ui: &mut Ui) {
|
||||
show_menu_bar(ui, &mut self.open_windows);
|
||||
ui.add(Separator::new());
|
||||
self.windows(ui.ctx());
|
||||
}
|
||||
|
||||
|
@ -279,7 +278,8 @@ struct Widgets {
|
|||
count: usize,
|
||||
radio: usize,
|
||||
slider_value: usize,
|
||||
text_inputs: [String; 3],
|
||||
single_line_text_input: String,
|
||||
multiline_text_input: String,
|
||||
}
|
||||
|
||||
impl Default for Widgets {
|
||||
|
@ -289,7 +289,8 @@ impl Default for Widgets {
|
|||
radio: 0,
|
||||
count: 0,
|
||||
slider_value: 100,
|
||||
text_inputs: ["Hello".to_string(), "World".to_string(), "".to_string()],
|
||||
single_line_text_input: "Hello World!".to_owned(),
|
||||
multiline_text_input: "Text can both be so wide that it needs a linebreak, but you can also add manual linebreak by pressing enter, creating new paragraphs.\nThis is the start of the next paragraph.\n\nClick me to edit me!".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -318,7 +319,7 @@ impl Widgets {
|
|||
}
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.inner_layout(Layout::horizontal(Align::Center), |ui| {
|
||||
if ui
|
||||
.add(Button::new("Click me"))
|
||||
.tooltip_text("This will just increase a counter.")
|
||||
|
@ -334,12 +335,17 @@ impl Widgets {
|
|||
self.slider_value *= 2;
|
||||
}
|
||||
|
||||
for (i, text) in self.text_inputs.iter_mut().enumerate() {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(label!("Text input {}: ", i));
|
||||
ui.add(TextEdit::new(text).id(i));
|
||||
}); // TODO: .tooltip_text("Enter text to edit me")
|
||||
}
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(label!("Single line text input:"));
|
||||
ui.add(
|
||||
TextEdit::new(&mut self.single_line_text_input)
|
||||
.multiline(false)
|
||||
.id("single line"),
|
||||
);
|
||||
}); // TODO: .tooltip_text("Enter text to edit me")
|
||||
|
||||
ui.add(label!("Multiline text input:"));
|
||||
ui.add(TextEdit::new(&mut self.multiline_text_input).id("multiline"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,46 +418,42 @@ impl Painting {
|
|||
self.lines.clear();
|
||||
}
|
||||
|
||||
ui.add_custom_contents(vec2(f32::INFINITY, 200.0), |ui| {
|
||||
let interact = ui.reserve_space(ui.available_finite().size(), Some(ui.id()));
|
||||
let rect = interact.rect;
|
||||
ui.set_clip_rect(ui.clip_rect().intersect(rect)); // Make sure we don't paint out of bounds
|
||||
Resize::default()
|
||||
.default_height(200.0)
|
||||
.show(ui, |ui| self.content(ui));
|
||||
}
|
||||
|
||||
if self.lines.is_empty() {
|
||||
self.lines.push(vec![]);
|
||||
}
|
||||
fn content(&mut self, ui: &mut Ui) {
|
||||
let interact = ui.reserve_space(ui.available_finite().size(), Some(ui.id()));
|
||||
let rect = interact.rect;
|
||||
ui.set_clip_rect(ui.clip_rect().intersect(rect)); // Make sure we don't paint out of bounds
|
||||
|
||||
let current_line = self.lines.last_mut().unwrap();
|
||||
if self.lines.is_empty() {
|
||||
self.lines.push(vec![]);
|
||||
}
|
||||
|
||||
if interact.active {
|
||||
if let Some(mouse_pos) = ui.input().mouse_pos {
|
||||
let canvas_pos = mouse_pos - rect.min;
|
||||
if current_line.last() != Some(&canvas_pos) {
|
||||
current_line.push(canvas_pos);
|
||||
}
|
||||
}
|
||||
} else if !current_line.is_empty() {
|
||||
self.lines.push(vec![]);
|
||||
}
|
||||
let current_line = self.lines.last_mut().unwrap();
|
||||
|
||||
for line in &self.lines {
|
||||
if line.len() >= 2 {
|
||||
ui.add_paint_cmd(PaintCmd::LinePath {
|
||||
points: line.iter().map(|p| rect.min + *p).collect(),
|
||||
color: LIGHT_GRAY,
|
||||
width: 2.0,
|
||||
});
|
||||
if interact.active {
|
||||
if let Some(mouse_pos) = ui.input().mouse_pos {
|
||||
let canvas_pos = mouse_pos - rect.min;
|
||||
if current_line.last() != Some(&canvas_pos) {
|
||||
current_line.push(canvas_pos);
|
||||
}
|
||||
}
|
||||
} else if !current_line.is_empty() {
|
||||
self.lines.push(vec![]);
|
||||
}
|
||||
|
||||
// Frame it:
|
||||
ui.add_paint_cmd(PaintCmd::Rect {
|
||||
rect: ui.rect(),
|
||||
corner_radius: 0.0,
|
||||
fill_color: None,
|
||||
outline: Some(Outline::new(1.0, WHITE)),
|
||||
});
|
||||
});
|
||||
for line in &self.lines {
|
||||
if line.len() >= 2 {
|
||||
ui.add_paint_cmd(PaintCmd::LinePath {
|
||||
points: line.iter().map(|p| rect.min + *p).collect(),
|
||||
color: LIGHT_GRAY,
|
||||
width: 2.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ pub type FontDefinitions = BTreeMap<TextStyle, (FontFamily, f32)>;
|
|||
|
||||
pub fn default_font_definitions() -> FontDefinitions {
|
||||
let mut definitions = FontDefinitions::new();
|
||||
definitions.insert(TextStyle::Body, (FontFamily::VariableWidth, 16.0));
|
||||
definitions.insert(TextStyle::Button, (FontFamily::VariableWidth, 18.0));
|
||||
definitions.insert(TextStyle::Heading, (FontFamily::VariableWidth, 28.0));
|
||||
definitions.insert(TextStyle::Body, (FontFamily::VariableWidth, 14.0));
|
||||
definitions.insert(TextStyle::Button, (FontFamily::VariableWidth, 16.0));
|
||||
definitions.insert(TextStyle::Heading, (FontFamily::VariableWidth, 24.0));
|
||||
definitions.insert(TextStyle::Monospace, (FontFamily::Monospace, 13.0));
|
||||
definitions
|
||||
}
|
||||
|
|
|
@ -143,6 +143,12 @@ pub struct PathPoint {
|
|||
pub struct Path(Vec<PathPoint>);
|
||||
|
||||
impl Path {
|
||||
pub fn from_point_loop(points: &[Pos2]) -> Self {
|
||||
let mut path = Self::default();
|
||||
path.add_line_loop(points);
|
||||
path
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
@ -153,7 +159,8 @@ impl Path {
|
|||
}
|
||||
|
||||
pub fn add_circle(&mut self, center: Pos2, radius: f32) {
|
||||
let n = 32; // TODO: parameter
|
||||
let n = (radius * 2.0).round() as i32; // TODO: tweak a bit more
|
||||
let n = clamp(n, 4..=64);
|
||||
for i in 0..n {
|
||||
let angle = remap(i as f32, 0.0..=n as f32, 0.0..=TAU);
|
||||
let normal = vec2(angle.cos(), angle.sin());
|
||||
|
@ -167,6 +174,7 @@ impl Path {
|
|||
self.add_point(points[1], normal);
|
||||
}
|
||||
|
||||
// TODO: make it clear it is an open (non-closed) thing.
|
||||
pub fn add_line(&mut self, points: &[Pos2]) {
|
||||
let n = points.len();
|
||||
assert!(n >= 2);
|
||||
|
@ -177,8 +185,8 @@ impl Path {
|
|||
} else {
|
||||
self.add_point(points[0], (points[1] - points[0]).normalized().rot90());
|
||||
for i in 1..n - 1 {
|
||||
let n0 = (points[i] - points[i - 1]).normalized().rot90();
|
||||
let n1 = (points[i + 1] - points[i]).normalized().rot90();
|
||||
let n0 = (points[i] - points[i - 1]).normalized().rot90(); // TODO: don't calculate each normal twice!
|
||||
let n1 = (points[i + 1] - points[i]).normalized().rot90(); // TODO: don't calculate each normal twice!
|
||||
let v = (n0 + n1) / 2.0;
|
||||
let normal = v / v.length_sq();
|
||||
self.add_point(points[i], normal); // TODO: handle VERY sharp turns better
|
||||
|
@ -190,6 +198,20 @@ impl Path {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn add_line_loop(&mut self, points: &[Pos2]) {
|
||||
let n = points.len();
|
||||
assert!(n >= 2);
|
||||
|
||||
// TODO: optimize
|
||||
for i in 0..n {
|
||||
let n0 = (points[i] - points[(i + n - 1) % n]).normalized().rot90();
|
||||
let n1 = (points[(i + 1) % n] - points[i]).normalized().rot90();
|
||||
let v = (n0 + n1) / 2.0;
|
||||
let normal = v / v.length_sq();
|
||||
self.add_point(points[i], normal); // TODO: handle VERY sharp turns better
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_rectangle(&mut self, rect: Rect) {
|
||||
let min = rect.min;
|
||||
let max = rect.max;
|
||||
|
@ -229,7 +251,8 @@ impl Path {
|
|||
/// quadrant 3 up rigth
|
||||
/// 4 * TAU / 4 = right
|
||||
pub fn add_circle_quadrant(&mut self, center: Pos2, radius: f32, quadrant: f32) {
|
||||
let n = 8;
|
||||
let n = (radius * 0.5).round() as i32; // TODO: tweak a bit more
|
||||
let n = clamp(n, 2..=32);
|
||||
const RIGHT_ANGLE: f32 = TAU / 4.0;
|
||||
for i in 0..=n {
|
||||
let angle = remap(
|
||||
|
@ -570,6 +593,9 @@ pub fn mesh_command(
|
|||
color,
|
||||
} => {
|
||||
galley.sanity_check();
|
||||
|
||||
let text_offset = vec2(0.0, 1.0); // Eye-balled for buttons. TODO: why is this needed?
|
||||
|
||||
let font = &fonts[text_style];
|
||||
let mut chars = galley.text.chars();
|
||||
for line in &galley.lines {
|
||||
|
@ -577,7 +603,7 @@ pub fn mesh_command(
|
|||
let c = chars.next().unwrap();
|
||||
if let Some(glyph) = font.uv_rect(c) {
|
||||
let mut top_left = Vertex {
|
||||
pos: pos + glyph.offset + vec2(*x_offset, line.y_min),
|
||||
pos: pos + glyph.offset + vec2(*x_offset, line.y_min) + text_offset,
|
||||
uv: glyph.min,
|
||||
color,
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ use serde_derive::{Deserialize, Serialize};
|
|||
|
||||
use crate::{color::*, math::*, types::*};
|
||||
|
||||
// TODO: split into Spacing and Style?
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub struct Style {
|
||||
/// Horizontal and vertical padding within a window frame.
|
||||
|
@ -25,13 +26,24 @@ pub struct Style {
|
|||
/// The text starts after this many pixels.
|
||||
pub start_icon_width: f32,
|
||||
|
||||
pub interact: Interact,
|
||||
|
||||
// -----------------------------------------------
|
||||
// Purely visual:
|
||||
pub interact: Interact,
|
||||
|
||||
// TODO: an WidgetStyle ?
|
||||
pub text_color: Color,
|
||||
|
||||
/// For stuff like check marks in check boxes.
|
||||
pub line_width: f32,
|
||||
|
||||
pub thin_outline: Outline,
|
||||
|
||||
/// e.g. the background of windows
|
||||
pub background_fill_color: Color,
|
||||
|
||||
/// e.g. the background of the slider or text edit
|
||||
pub dark_bg_color: Color,
|
||||
|
||||
pub cursor_blink_hz: f32,
|
||||
pub text_cursor_width: f32,
|
||||
|
||||
|
@ -59,12 +71,16 @@ impl Default for Style {
|
|||
item_spacing: vec2(8.0, 4.0),
|
||||
indent: 21.0,
|
||||
clickable_diameter: 22.0,
|
||||
start_icon_width: 16.0,
|
||||
start_icon_width: 14.0,
|
||||
interact: Default::default(),
|
||||
text_color: gray(160, 255),
|
||||
line_width: 1.0,
|
||||
thin_outline: Outline::new(0.5, GRAY),
|
||||
background_fill_color: gray(32, 250),
|
||||
dark_bg_color: gray(0, 140),
|
||||
cursor_blink_hz: 1.0,
|
||||
text_cursor_width: 2.0,
|
||||
animation_time: 1.0 / 20.0,
|
||||
animation_time: 1.0 / 15.0,
|
||||
window: Window::default(),
|
||||
menu_bar: MenuBar::default(),
|
||||
clip_rect_margin: 3.0,
|
||||
|
@ -84,24 +100,58 @@ impl Default for Interact {
|
|||
fn default() -> Self {
|
||||
Self {
|
||||
active: WidgetStyle {
|
||||
fill_color: Some(srgba(120, 120, 200, 255)),
|
||||
bg_fill_color: None,
|
||||
fill_color: srgba(120, 120, 200, 255),
|
||||
stroke_color: WHITE,
|
||||
stroke_width: 2.0,
|
||||
outline: Some(Outline::new(2.0, WHITE)),
|
||||
rect_outline: Some(Outline::new(1.0, WHITE)),
|
||||
corner_radius: 5.0,
|
||||
},
|
||||
hovered: WidgetStyle {
|
||||
fill_color: Some(srgba(100, 100, 150, 255)),
|
||||
bg_fill_color: None,
|
||||
fill_color: srgba(100, 100, 150, 255),
|
||||
stroke_color: WHITE,
|
||||
stroke_width: 1.5,
|
||||
outline: None,
|
||||
rect_outline: Some(Outline::new(1.0, WHITE)),
|
||||
corner_radius: 5.0,
|
||||
},
|
||||
inactive: WidgetStyle {
|
||||
fill_color: Some(srgba(60, 60, 80, 255)),
|
||||
bg_fill_color: None,
|
||||
fill_color: srgba(60, 60, 80, 255),
|
||||
stroke_color: gray(210, 255), // Mustn't look grayed out!
|
||||
stroke_width: 1.0,
|
||||
rect_outline: Some(Outline::new(0.5, WHITE)),
|
||||
corner_radius: 0.0,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Interact {
|
||||
pub fn classic() -> Self {
|
||||
Self {
|
||||
active: WidgetStyle {
|
||||
bg_fill_color: Some(srgba(120, 120, 200, 255)),
|
||||
fill_color: srgba(120, 120, 200, 255),
|
||||
stroke_color: WHITE,
|
||||
stroke_width: 2.0,
|
||||
rect_outline: Some(Outline::new(2.0, WHITE)),
|
||||
corner_radius: 5.0,
|
||||
},
|
||||
hovered: WidgetStyle {
|
||||
bg_fill_color: Some(srgba(100, 100, 150, 255)),
|
||||
fill_color: srgba(100, 100, 150, 255),
|
||||
stroke_color: WHITE,
|
||||
stroke_width: 1.5,
|
||||
rect_outline: None,
|
||||
corner_radius: 5.0,
|
||||
},
|
||||
inactive: WidgetStyle {
|
||||
bg_fill_color: Some(srgba(60, 60, 80, 255)),
|
||||
fill_color: srgba(60, 60, 80, 255),
|
||||
stroke_color: gray(220, 255), // Mustn't look grayed out!
|
||||
stroke_width: 1.0,
|
||||
outline: None,
|
||||
rect_outline: None,
|
||||
corner_radius: 0.0,
|
||||
},
|
||||
}
|
||||
|
@ -122,8 +172,12 @@ impl Interact {
|
|||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
pub struct WidgetStyle {
|
||||
/// Fill color of the interactive part of a component (button, slider grab, checkbox, ...)
|
||||
pub fill_color: Option<Color>,
|
||||
/// Background color of widget
|
||||
pub bg_fill_color: Option<Color>,
|
||||
|
||||
/// Fill color of the interactive part of a component (slider grab, checkbox, ...)
|
||||
/// When you need a fill_color.
|
||||
pub fill_color: Color,
|
||||
|
||||
/// Stroke and text color of the interactive part of a component (button, slider grab, checkbox, ...)
|
||||
pub stroke_color: Color,
|
||||
|
@ -131,8 +185,9 @@ pub struct WidgetStyle {
|
|||
/// For lines etc
|
||||
pub stroke_width: f32,
|
||||
|
||||
/// For rectangles
|
||||
pub outline: Option<Outline>,
|
||||
/// For surrounding rectangle of things that need it,
|
||||
/// like buttons, the box of the checkbox, etc.
|
||||
pub rect_outline: Option<Outline>,
|
||||
|
||||
/// Button frames etdc
|
||||
pub corner_radius: f32,
|
||||
|
@ -163,15 +218,6 @@ impl Default for MenuBar {
|
|||
}
|
||||
|
||||
impl Style {
|
||||
/// e.g. the background of the slider
|
||||
pub fn background_fill_color(&self) -> Color {
|
||||
gray(34, 250)
|
||||
}
|
||||
|
||||
pub fn text_color(&self) -> Color {
|
||||
gray(255, 200)
|
||||
}
|
||||
|
||||
/// Use this style for interactive things
|
||||
pub fn interact(&self, interact: &InteractInfo) -> &WidgetStyle {
|
||||
self.interact.style(interact)
|
||||
|
@ -185,7 +231,9 @@ impl Style {
|
|||
vec2(box_side, box_side),
|
||||
);
|
||||
|
||||
let small_icon_rect = Rect::from_center_size(big_icon_rect.center(), vec2(10.0, 10.0));
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -135,6 +135,7 @@ pub enum PaintCmd {
|
|||
color: Color,
|
||||
width: f32,
|
||||
},
|
||||
// TODO: remove. Just have Path.
|
||||
LinePath {
|
||||
points: Vec<Pos2>,
|
||||
color: Color,
|
||||
|
|
|
@ -473,7 +473,7 @@ impl Ui {
|
|||
text_style: TextStyle,
|
||||
color: Option<Color>,
|
||||
) {
|
||||
let color = color.unwrap_or_else(|| self.style().text_color());
|
||||
let color = color.unwrap_or_else(|| self.style().text_color);
|
||||
self.add_paint_cmd(PaintCmd::Text {
|
||||
pos,
|
||||
galley,
|
||||
|
|
|
@ -226,13 +226,12 @@ impl Widget for Button {
|
|||
let mut size = galley.size + 2.0 * padding;
|
||||
size.y = size.y.max(ui.style().clickable_diameter);
|
||||
let interact = ui.reserve_space(size, Some(id));
|
||||
let mut text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
|
||||
text_cursor.y += 2.0; // TODO: why is this needed?
|
||||
let fill_color = fill_color.or(ui.style().interact(&interact).fill_color);
|
||||
let text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * galley.size.y);
|
||||
let bg_fill_color = fill_color.or(ui.style().interact(&interact).bg_fill_color);
|
||||
ui.add_paint_cmd(PaintCmd::Rect {
|
||||
corner_radius: ui.style().interact(&interact).corner_radius,
|
||||
fill_color: fill_color,
|
||||
outline: ui.style().interact(&interact).outline,
|
||||
fill_color: bg_fill_color,
|
||||
outline: ui.style().interact(&interact).rect_outline,
|
||||
rect: interact.rect,
|
||||
});
|
||||
let stroke_color = ui.style().interact(&interact).stroke_color;
|
||||
|
@ -286,9 +285,9 @@ impl<'a> Widget for Checkbox<'a> {
|
|||
}
|
||||
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
|
||||
ui.add_paint_cmd(PaintCmd::Rect {
|
||||
corner_radius: 3.0,
|
||||
fill_color: ui.style().interact(&interact).fill_color,
|
||||
outline: None,
|
||||
corner_radius: ui.style().interact(&interact).corner_radius,
|
||||
fill_color: ui.style().interact(&interact).bg_fill_color,
|
||||
outline: ui.style().interact(&interact).rect_outline,
|
||||
rect: big_icon_rect,
|
||||
});
|
||||
|
||||
|
@ -356,15 +355,15 @@ impl Widget for RadioButton {
|
|||
let text_cursor =
|
||||
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
|
||||
|
||||
let fill_color = ui.style().interact(&interact).fill_color;
|
||||
let bg_fill_color = ui.style().interact(&interact).bg_fill_color;
|
||||
let stroke_color = ui.style().interact(&interact).stroke_color;
|
||||
|
||||
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
|
||||
|
||||
ui.add_paint_cmd(PaintCmd::Circle {
|
||||
center: big_icon_rect.center(),
|
||||
fill_color,
|
||||
outline: None,
|
||||
fill_color: bg_fill_color,
|
||||
outline: ui.style().interact(&interact).rect_outline, // TODO
|
||||
radius: big_icon_rect.width() / 2.0,
|
||||
});
|
||||
|
||||
|
@ -373,7 +372,7 @@ impl Widget for RadioButton {
|
|||
center: small_icon_rect.center(),
|
||||
fill_color: Some(stroke_color),
|
||||
outline: None,
|
||||
radius: small_icon_rect.width() / 2.0,
|
||||
radius: small_icon_rect.width() / 3.0,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -386,7 +385,7 @@ impl Widget for RadioButton {
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
pub struct Separator {
|
||||
line_width: f32,
|
||||
line_width: Option<f32>,
|
||||
min_spacing: f32,
|
||||
extra: f32,
|
||||
color: Color,
|
||||
|
@ -395,7 +394,7 @@ pub struct Separator {
|
|||
impl Separator {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
line_width: 2.0,
|
||||
line_width: None,
|
||||
min_spacing: 6.0,
|
||||
extra: 0.0,
|
||||
color: color::WHITE,
|
||||
|
@ -403,7 +402,7 @@ impl Separator {
|
|||
}
|
||||
|
||||
pub fn line_width(mut self, line_width: f32) -> Self {
|
||||
self.line_width = line_width;
|
||||
self.line_width = Some(line_width);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -426,26 +425,36 @@ impl Separator {
|
|||
|
||||
impl Widget for Separator {
|
||||
fn ui(self, ui: &mut Ui) -> GuiResponse {
|
||||
let Separator {
|
||||
line_width,
|
||||
min_spacing,
|
||||
extra,
|
||||
color,
|
||||
} = self;
|
||||
|
||||
let line_width = line_width.unwrap_or_else(|| ui.style().line_width);
|
||||
|
||||
let available_space = ui.available_finite().size();
|
||||
|
||||
let extra = self.extra;
|
||||
let (points, interact) = match ui.layout().dir() {
|
||||
Direction::Horizontal => {
|
||||
let interact = ui.reserve_space(vec2(self.min_spacing, available_space.y), None);
|
||||
let interact = ui.reserve_space(vec2(min_spacing, available_space.y), None);
|
||||
let r = &interact.rect;
|
||||
(
|
||||
[
|
||||
pos2(interact.rect.center().x, interact.rect.top() - extra),
|
||||
pos2(interact.rect.center().x, interact.rect.bottom() + extra),
|
||||
pos2(r.center().x, r.top() - extra),
|
||||
pos2(r.center().x, r.bottom() + extra),
|
||||
],
|
||||
interact,
|
||||
)
|
||||
}
|
||||
Direction::Vertical => {
|
||||
let interact = ui.reserve_space(vec2(available_space.x, self.min_spacing), None);
|
||||
let interact = ui.reserve_space(vec2(available_space.x, min_spacing), None);
|
||||
let r = &interact.rect;
|
||||
(
|
||||
[
|
||||
pos2(interact.rect.left() - extra, interact.rect.center().y),
|
||||
pos2(interact.rect.right() + extra, interact.rect.center().y),
|
||||
pos2(r.left() - extra, r.center().y),
|
||||
pos2(r.right() + extra, r.center().y),
|
||||
],
|
||||
interact,
|
||||
)
|
||||
|
@ -453,8 +462,8 @@ impl Widget for Separator {
|
|||
};
|
||||
ui.add_paint_cmd(PaintCmd::LineSegment {
|
||||
points,
|
||||
color: self.color,
|
||||
width: self.line_width,
|
||||
color: color,
|
||||
width: line_width,
|
||||
});
|
||||
ui.response(interact)
|
||||
}
|
||||
|
|
|
@ -176,14 +176,14 @@ impl<'a> Widget for Slider<'a> {
|
|||
ui.add_paint_cmd(PaintCmd::Rect {
|
||||
rect: rail_rect,
|
||||
corner_radius: rail_radius,
|
||||
fill_color: Some(ui.style().background_fill_color()),
|
||||
fill_color: Some(ui.style().background_fill_color),
|
||||
outline: Some(Outline::new(1.0, color::gray(200, 255))), // TODO
|
||||
});
|
||||
|
||||
ui.add_paint_cmd(PaintCmd::Circle {
|
||||
center: pos2(marker_center_x, rail_rect.center().y),
|
||||
radius: handle_radius,
|
||||
fill_color: ui.style().interact(&interact).fill_color,
|
||||
fill_color: Some(ui.style().interact(&interact).fill_color),
|
||||
outline: Some(Outline::new(
|
||||
ui.style().interact(&interact).stroke_width,
|
||||
ui.style().interact(&interact).stroke_color,
|
||||
|
|
|
@ -13,6 +13,7 @@ pub struct TextEdit<'t> {
|
|||
id: Option<Id>,
|
||||
text_style: TextStyle, // TODO: Option<TextStyle>, where None means "use the default for the current Ui"
|
||||
text_color: Option<Color>,
|
||||
multiline: bool,
|
||||
}
|
||||
|
||||
impl<'t> TextEdit<'t> {
|
||||
|
@ -22,6 +23,7 @@ impl<'t> TextEdit<'t> {
|
|||
id: None,
|
||||
text_style: TextStyle::Body,
|
||||
text_color: Default::default(),
|
||||
multiline: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +41,11 @@ impl<'t> TextEdit<'t> {
|
|||
self.text_color = Some(text_color);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn multiline(mut self, multiline: bool) -> Self {
|
||||
self.multiline = multiline;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> Widget for TextEdit<'t> {
|
||||
|
@ -48,6 +55,7 @@ impl<'t> Widget for TextEdit<'t> {
|
|||
id,
|
||||
text_style,
|
||||
text_color,
|
||||
multiline,
|
||||
} = self;
|
||||
|
||||
let id = ui.make_child_id(id);
|
||||
|
@ -57,7 +65,11 @@ impl<'t> Widget for TextEdit<'t> {
|
|||
let font = &ui.fonts()[text_style];
|
||||
let line_spacing = font.line_spacing();
|
||||
let available_width = ui.available().width();
|
||||
let mut galley = font.layout_multiline(text.as_str(), available_width);
|
||||
let mut galley = if multiline {
|
||||
font.layout_multiline(text.as_str(), available_width)
|
||||
} else {
|
||||
font.layout_single_line(text.as_str())
|
||||
};
|
||||
let desired_size = galley.size.max(vec2(available_width, line_spacing));
|
||||
let interact = ui.reserve_space(desired_size, Some(id));
|
||||
|
||||
|
@ -95,19 +107,24 @@ impl<'t> Widget for TextEdit<'t> {
|
|||
|
||||
// layout again to avoid frame delay:
|
||||
let font = &ui.fonts()[text_style];
|
||||
galley = font.layout_multiline(text.as_str(), available_width);
|
||||
galley = if multiline {
|
||||
font.layout_multiline(text.as_str(), available_width)
|
||||
} else {
|
||||
font.layout_single_line(text.as_str())
|
||||
};
|
||||
|
||||
// dbg!(&galley);
|
||||
}
|
||||
|
||||
ui.add_paint_cmd(PaintCmd::Rect {
|
||||
rect: interact.rect,
|
||||
corner_radius: 0.0,
|
||||
// fill_color: Some(color::BLACK),
|
||||
fill_color: ui.style().interact(&interact).fill_color,
|
||||
// fill_color: Some(ui.style().background_fill_color()),
|
||||
outline: None, //Some(Outline::new(1.0, color::WHITE)),
|
||||
});
|
||||
{
|
||||
let bg_rect = interact.rect.expand(2.0); // breathing room for content
|
||||
ui.add_paint_cmd(PaintCmd::Rect {
|
||||
rect: bg_rect,
|
||||
corner_radius: ui.style().interact.style(&interact).corner_radius,
|
||||
fill_color: Some(ui.style().dark_bg_color),
|
||||
outline: ui.style().interact.style(&interact).rect_outline,
|
||||
});
|
||||
}
|
||||
|
||||
if has_kb_focus {
|
||||
let cursor_blink_hz = ui.style().cursor_blink_hz;
|
||||
|
|
Loading…
Reference in a new issue