Rename Region to Ui (shorter, sweeter)

This commit is contained in:
Emil Ernerfeldt 2020-05-08 22:42:31 +02:00
parent cbd51c3f43
commit fa82e8d806
22 changed files with 508 additions and 524 deletions

View file

@ -88,10 +88,7 @@ Add extremely quick animations for some things, maybe 2-3 frames. For instance:
* [ ] Solve which parts of Context are behind a mutex * [ ] Solve which parts of Context are behind a mutex
* [ ] All of Context behind one mutex? * [ ] All of Context behind one mutex?
* [ } Break up Context into Input, State, Output ? * [ } Break up Context into Input, State, Output ?
* [ ] Rename Region to something shorter? * [x] Rename Region to Ui
* `region: &Region` `region.add(...)` :/
* `gui: &Gui` `gui.add(...)` :)
* `ui: &Ui` `ui.add(...)` :)
* [ ] Maybe find a shorter name for the library like `egui`? * [ ] Maybe find a shorter name for the library like `egui`?
### Global widget search ### Global widget search

View file

@ -12,10 +12,10 @@ pub use {
// TODO // TODO
// pub trait Container { // pub trait Container {
// fn show(self, region: &mut Region, add_contents: impl FnOnce(&mut Region)); // fn show(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui));
// } // }
// pub trait Container { // pub trait Container {
// fn begin(&mut self, parent: &mut Region) -> Region; // fn begin(&mut self, parent: &mut Ui) -> Ui;
// fn end(self, parent: &mut Region, content: Region); // fn end(self, parent: &mut Ui, content: Ui);
// } // }

View file

@ -8,7 +8,7 @@ pub(crate) struct State {
#[serde(skip)] // Times are relative, and we don't want to continue animations anyway #[serde(skip)] // Times are relative, and we don't want to continue animations anyway
toggle_time: f64, toggle_time: f64,
/// Height open the region when open. Used for animations /// Height of the region when open. Used for animations
open_height: Option<f32>, open_height: Option<f32>,
} }
@ -44,9 +44,9 @@ impl CollapsingHeader {
} }
impl CollapsingHeader { impl CollapsingHeader {
pub fn show(self, region: &mut Region, add_contents: impl FnOnce(&mut Region)) -> GuiResponse { pub fn show(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) -> GuiResponse {
assert!( assert!(
region.direction() == Direction::Vertical, ui.direction() == Direction::Vertical,
"Horizontal collapsing is unimplemented" "Horizontal collapsing is unimplemented"
); );
let Self { let Self {
@ -57,63 +57,63 @@ impl CollapsingHeader {
// TODO: horizontal layout, with icon and text as labels. Inser background behind using Frame. // TODO: horizontal layout, with icon and text as labels. Inser background behind using Frame.
let title = &label.text; // TODO: not this let title = &label.text; // TODO: not this
let id = region.make_unique_id(title); let id = ui.make_unique_id(title);
let text_pos = region.cursor() + vec2(region.style().indent, 0.0); let text_pos = ui.cursor() + vec2(ui.style().indent, 0.0);
let (title, text_size) = label.layout(text_pos, region); let (title, text_size) = label.layout(text_pos, ui);
let text_max_x = text_pos.x + text_size.x; let text_max_x = text_pos.x + text_size.x;
let desired_width = region.available_width().max(text_max_x - region.cursor().x); let desired_width = ui.available_width().max(text_max_x - ui.cursor().x);
let interact = region.reserve_space( let interact = ui.reserve_space(
vec2( vec2(
desired_width, desired_width,
text_size.y + 2.0 * region.style().button_padding.y, text_size.y + 2.0 * ui.style().button_padding.y,
), ),
Some(id), Some(id),
); );
let text_pos = pos2(text_pos.x, interact.rect.center().y - text_size.y / 2.0); let text_pos = pos2(text_pos.x, interact.rect.center().y - text_size.y / 2.0);
let mut state = { let mut state = {
let mut memory = region.memory(); let mut memory = ui.memory();
let mut state = memory.collapsing_headers.entry(id).or_insert(State { let mut state = memory.collapsing_headers.entry(id).or_insert(State {
open: default_open, open: default_open,
..Default::default() ..Default::default()
}); });
if interact.clicked { if interact.clicked {
state.open = !state.open; state.open = !state.open;
state.toggle_time = region.input().time; state.toggle_time = ui.input().time;
} }
*state *state
}; };
let where_to_put_background = region.paint_list_len(); let where_to_put_background = ui.paint_list_len();
paint_icon(region, &state, &interact); paint_icon(ui, &state, &interact);
region.add_text( ui.add_text(
text_pos, text_pos,
label.text_style, label.text_style,
title, title,
Some(region.style().interact_stroke_color(&interact)), Some(ui.style().interact_stroke_color(&interact)),
); );
region.insert_paint_cmd( ui.insert_paint_cmd(
where_to_put_background, where_to_put_background,
PaintCmd::Rect { PaintCmd::Rect {
corner_radius: region.style().interact_corner_radius(&interact), corner_radius: ui.style().interact_corner_radius(&interact),
fill_color: region.style().interact_fill_color(&interact), fill_color: ui.style().interact_fill_color(&interact),
outline: region.style().interact_outline(&interact), outline: ui.style().interact_outline(&interact),
rect: interact.rect, rect: interact.rect,
}, },
); );
region.expand_to_include_child(interact.rect); // TODO: remove, just a test ui.expand_to_include_child(interact.rect); // TODO: remove, just a test
let animation_time = region.style().animation_time; let animation_time = ui.style().animation_time;
let time_since_toggle = (region.input().time - state.toggle_time) as f32; let time_since_toggle = (ui.input().time - state.toggle_time) as f32;
let time_since_toggle = time_since_toggle + region.input().dt; // Instant feedback let time_since_toggle = time_since_toggle + ui.input().dt; // Instant feedback
let animate = time_since_toggle < animation_time; let animate = time_since_toggle < animation_time;
if animate { if animate {
region.indent(id, |child_region| { ui.indent(id, |child_ui| {
let max_height = if state.open { let max_height = if state.open {
let full_height = state.open_height.unwrap_or(1000.0); let full_height = state.open_height.unwrap_or(1000.0);
remap(time_since_toggle, 0.0..=animation_time, 0.0..=full_height) remap(time_since_toggle, 0.0..=animation_time, 0.0..=full_height)
@ -122,42 +122,42 @@ impl CollapsingHeader {
remap_clamp(time_since_toggle, 0.0..=animation_time, full_height..=0.0) remap_clamp(time_since_toggle, 0.0..=animation_time, full_height..=0.0)
}; };
let mut clip_rect = child_region.clip_rect(); let mut clip_rect = child_ui.clip_rect();
clip_rect.max.y = clip_rect.max.y.min(child_region.cursor().y + max_height); clip_rect.max.y = clip_rect.max.y.min(child_ui.cursor().y + max_height);
child_region.set_clip_rect(clip_rect); child_ui.set_clip_rect(clip_rect);
let top_left = child_region.top_left(); let top_left = child_ui.top_left();
add_contents(child_region); add_contents(child_ui);
state.open_height = Some(child_region.bounding_size().y); state.open_height = Some(child_ui.bounding_size().y);
// Pretend children took up less space: // Pretend children took up less space:
let mut child_bounds = child_region.child_bounds(); let mut child_bounds = child_ui.child_bounds();
child_bounds.max.y = child_bounds.max.y.min(top_left.y + max_height); child_bounds.max.y = child_bounds.max.y.min(top_left.y + max_height);
child_region.force_set_child_bounds(child_bounds); child_ui.force_set_child_bounds(child_bounds);
}); });
} else if state.open { } else if state.open {
let full_size = region.indent(id, add_contents).rect.size(); let full_size = ui.indent(id, add_contents).rect.size();
state.open_height = Some(full_size.y); state.open_height = Some(full_size.y);
} }
region.memory().collapsing_headers.insert(id, state); ui.memory().collapsing_headers.insert(id, state);
region.response(interact) ui.response(interact)
} }
} }
fn paint_icon(region: &mut Region, state: &State, interact: &InteractInfo) { fn paint_icon(ui: &mut Ui, state: &State, interact: &InteractInfo) {
let stroke_color = region.style().interact_stroke_color(interact); let stroke_color = ui.style().interact_stroke_color(interact);
let stroke_width = region.style().interact_stroke_width(interact); let stroke_width = ui.style().interact_stroke_width(interact);
let (mut small_icon_rect, _) = region.style().icon_rectangles(interact.rect); let (mut small_icon_rect, _) = ui.style().icon_rectangles(interact.rect);
small_icon_rect.set_center(pos2( small_icon_rect.set_center(pos2(
interact.rect.left() + region.style().indent / 2.0, interact.rect.left() + ui.style().indent / 2.0,
interact.rect.center().y, interact.rect.center().y,
)); ));
// Draw a minus: // Draw a minus:
region.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::Line {
points: vec![ points: vec![
pos2(small_icon_rect.left(), small_icon_rect.center().y), pos2(small_icon_rect.left(), small_icon_rect.center().y),
pos2(small_icon_rect.right(), small_icon_rect.center().y), pos2(small_icon_rect.right(), small_icon_rect.center().y),
@ -168,7 +168,7 @@ fn paint_icon(region: &mut Region, state: &State, interact: &InteractInfo) {
if !state.open { if !state.open {
// Draw it as a plus: // Draw it as a plus:
region.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::Line {
points: vec![ points: vec![
pos2(small_icon_rect.center().x, small_icon_rect.top()), pos2(small_icon_rect.center().x, small_icon_rect.top()),
pos2(small_icon_rect.center().x, small_icon_rect.bottom()), pos2(small_icon_rect.center().x, small_icon_rect.bottom()),

View file

@ -1,4 +1,4 @@
//! A Floating is a region that has no parent, it floats on the background. //! A Floating is an Ui that has no parent, it floats on the background.
//! It is potentioally movable. //! It is potentioally movable.
//! It has no frame or own size. //! It has no frame or own size.
//! It is the foundation for a window //! It is the foundation for a window
@ -49,7 +49,7 @@ impl Floating {
} }
impl Floating { impl Floating {
pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Region)) { pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Ui)) {
let default_pos = self.default_pos.unwrap_or_else(|| pos2(100.0, 100.0)); // TODO let default_pos = self.default_pos.unwrap_or_else(|| pos2(100.0, 100.0)); // TODO
let id = ctx.register_unique_id(self.id, "Floating", default_pos); let id = ctx.register_unique_id(self.id, "Floating", default_pos);
let layer = Layer::Window(id); let layer = Layer::Window(id);
@ -67,14 +67,14 @@ impl Floating {
}; };
state.pos = state.pos.round(); state.pos = state.pos.round();
let mut region = Region::new( let mut ui = Ui::new(
ctx.clone(), ctx.clone(),
layer, layer,
id, id,
Rect::from_min_size(state.pos, Vec2::infinity()), Rect::from_min_size(state.pos, Vec2::infinity()),
); );
add_contents(&mut region); add_contents(&mut ui);
state.size = region.bounding_size().ceil(); state.size = ui.bounding_size().ceil();
let rect = Rect::from_min_size(state.pos, state.size); let rect = Rect::from_min_size(state.pos, state.size);
let clip_rect = Rect::everything(); let clip_rect = Rect::everything();

View file

@ -15,26 +15,26 @@ impl Frame {
} }
impl Frame { impl Frame {
pub fn show(self, region: &mut Region, add_contents: impl FnOnce(&mut Region)) { pub fn show(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
let style = region.style(); let style = ui.style();
let margin = self.margin.unwrap_or_default(); let margin = self.margin.unwrap_or_default();
let outer_pos = region.cursor(); let outer_pos = ui.cursor();
let inner_rect = let inner_rect =
Rect::from_min_size(outer_pos + margin, region.available_space() - 2.0 * margin); Rect::from_min_size(outer_pos + margin, ui.available_space() - 2.0 * margin);
let where_to_put_background = region.paint_list_len(); let where_to_put_background = ui.paint_list_len();
let mut child_region = region.child_region(inner_rect); let mut child_ui = ui.child_ui(inner_rect);
add_contents(&mut child_region); add_contents(&mut child_ui);
let inner_size = child_region.bounding_size(); let inner_size = child_ui.bounding_size();
let inner_size = inner_size.ceil(); // TODO: round to pixel let inner_size = inner_size.ceil(); // TODO: round to pixel
let outer_rect = Rect::from_min_size(outer_pos, margin + inner_size + margin); let outer_rect = Rect::from_min_size(outer_pos, margin + inner_size + margin);
let corner_radius = style.window.corner_radius; let corner_radius = style.window.corner_radius;
let fill_color = style.background_fill_color(); let fill_color = style.background_fill_color();
region.insert_paint_cmd( ui.insert_paint_cmd(
where_to_put_background, where_to_put_background,
PaintCmd::Rect { PaintCmd::Rect {
corner_radius, corner_radius,
@ -44,7 +44,7 @@ impl Frame {
}, },
); );
region.expand_to_include_child(child_region.child_bounds().expand2(margin)); ui.expand_to_include_child(child_ui.child_bounds().expand2(margin));
// TODO: move up cursor? // TODO: move up cursor?
} }
} }

View file

@ -13,7 +13,7 @@ pub struct Resize {
/// If false, we are no enabled /// If false, we are no enabled
resizable: bool, resizable: bool,
// Will still try to stay within parent region bounds // Will still try to stay within parent ui bounds
min_size: Vec2, min_size: Vec2,
max_size: Vec2, max_size: Vec2,
@ -132,17 +132,17 @@ impl Resize {
// TODO: a common trait for Things that follow this pattern // TODO: a common trait for Things that follow this pattern
impl Resize { impl Resize {
pub fn show(mut self, region: &mut Region, add_contents: impl FnOnce(&mut Region)) { pub fn show(mut self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
if !self.resizable { if !self.resizable {
return add_contents(region); return add_contents(ui);
} }
let id = region.make_child_id("scroll"); let id = ui.make_child_id("scroll");
self.min_size = self.min_size.min(region.available_space()); self.min_size = self.min_size.min(ui.available_space());
self.max_size = self.max_size.min(region.available_space()); self.max_size = self.max_size.min(ui.available_space());
self.max_size = self.max_size.max(self.min_size); self.max_size = self.max_size.max(self.min_size);
let (is_new, mut state) = match region.memory().resize.get(&id) { let (is_new, mut state) = match ui.memory().resize.get(&id) {
Some(state) => (false, *state), Some(state) => (false, *state),
None => { None => {
let default_size = self.default_size.clamp(self.min_size..=self.max_size); let default_size = self.default_size.clamp(self.min_size..=self.max_size);
@ -153,7 +153,7 @@ impl Resize {
state.size = state.size.clamp(self.min_size..=self.max_size); state.size = state.size.clamp(self.min_size..=self.max_size);
let last_frame_size = state.size; let last_frame_size = state.size;
let position = region.cursor(); let position = ui.cursor();
// Resize-corner: // Resize-corner:
let corner_size = Vec2::splat(16.0); // TODO: style let corner_size = Vec2::splat(16.0); // TODO: style
@ -161,10 +161,10 @@ impl Resize {
position + state.size + self.handle_offset - corner_size, position + state.size + self.handle_offset - corner_size,
corner_size, corner_size,
); );
let corner_interact = region.interact_rect(corner_rect, id.with("corner")); let corner_interact = ui.interact_rect(corner_rect, id.with("corner"));
if corner_interact.active { if corner_interact.active {
if let Some(mouse_pos) = region.input().mouse_pos { if let Some(mouse_pos) = ui.input().mouse_pos {
// This is the desired size. We may not be able to achieve it. // This is the desired size. We may not be able to achieve it.
state.size = state.size =
@ -178,11 +178,11 @@ impl Resize {
// ------------------------------ // ------------------------------
let inner_rect = Rect::from_min_size(region.cursor(), state.size); let inner_rect = Rect::from_min_size(ui.cursor(), state.size);
let desired_size = { let desired_size = {
let mut content_clip_rect = region let mut content_clip_rect = ui
.clip_rect() .clip_rect()
.intersect(inner_rect.expand(region.style().clip_rect_margin)); .intersect(inner_rect.expand(ui.style().clip_rect_margin));
// If we pull the resize handle to shrink, we want to TRY to shink it. // If we pull the resize handle to shrink, we want to TRY to shink it.
// After laying out the contents, we might be much bigger. // After laying out the contents, we might be much bigger.
@ -192,12 +192,12 @@ impl Resize {
content_clip_rect.max = content_clip_rect content_clip_rect.max = content_clip_rect
.max .max
.max(content_clip_rect.min + last_frame_size) .max(content_clip_rect.min + last_frame_size)
.min(region.clip_rect().max); // Respect parent region .min(ui.clip_rect().max); // Respect parent region
let mut contents_region = region.child_region(inner_rect); let mut contents_ui = ui.child_ui(inner_rect);
contents_region.set_clip_rect(content_clip_rect); contents_ui.set_clip_rect(content_clip_rect);
add_contents(&mut contents_region); add_contents(&mut contents_ui);
contents_region.bounding_size() contents_ui.bounding_size()
}; };
let desired_size = desired_size.ceil(); // Avoid rounding errors in math let desired_size = desired_size.ceil(); // Avoid rounding errors in math
@ -220,29 +220,29 @@ impl Resize {
// state.size = state.size.clamp(self.min_size..=self.max_size); // state.size = state.size.clamp(self.min_size..=self.max_size);
state.size = state.size.round(); // TODO: round to pixels state.size = state.size.round(); // TODO: round to pixels
region.reserve_space(state.size, None); ui.reserve_space(state.size, None);
// ------------------------------ // ------------------------------
paint_resize_corner(region, &corner_interact); paint_resize_corner(ui, &corner_interact);
if corner_interact.hovered || corner_interact.active { if corner_interact.hovered || corner_interact.active {
region.ctx().output().cursor_icon = CursorIcon::ResizeNwSe; ui.ctx().output().cursor_icon = CursorIcon::ResizeNwSe;
} }
region.memory().resize.insert(id, state); ui.memory().resize.insert(id, state);
} }
} }
fn paint_resize_corner(region: &mut Region, interact: &InteractInfo) { fn paint_resize_corner(ui: &mut Ui, interact: &InteractInfo) {
let color = region.style().interact_stroke_color(interact); let color = ui.style().interact_stroke_color(interact);
let width = region.style().interact_stroke_width(interact); let width = ui.style().interact_stroke_width(interact);
let corner = interact.rect.right_bottom().round(); // TODO: round to pixels let corner = interact.rect.right_bottom().round(); // TODO: round to pixels
let mut w = 2.0; let mut w = 2.0;
while w < 12.0 { while w < 12.0 {
region.add_paint_cmd(PaintCmd::line_segment( ui.add_paint_cmd(PaintCmd::line_segment(
(pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)), (pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)),
color, color,
width, width,

View file

@ -45,10 +45,10 @@ impl ScrollArea {
} }
impl ScrollArea { impl ScrollArea {
pub fn show(self, outer_region: &mut Region, add_contents: impl FnOnce(&mut Region)) { pub fn show(self, outer_ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
let ctx = outer_region.ctx().clone(); let ctx = outer_ui.ctx().clone();
let scroll_area_id = outer_region.make_child_id("scroll_area"); let scroll_area_id = outer_ui.make_child_id("scroll_area");
let mut state = ctx let mut state = ctx
.memory() .memory()
.scroll_areas .scroll_areas
@ -69,23 +69,23 @@ impl ScrollArea {
}; };
let outer_size = vec2( let outer_size = vec2(
outer_region.available_width(), outer_ui.available_width(),
outer_region.available_height().min(self.max_height), outer_ui.available_height().min(self.max_height),
); );
let inner_size = outer_size - vec2(current_scroll_bar_width, 0.0); let inner_size = outer_size - vec2(current_scroll_bar_width, 0.0);
let inner_rect = Rect::from_min_size(outer_region.cursor(), inner_size); let inner_rect = Rect::from_min_size(outer_ui.cursor(), inner_size);
let mut content_region = outer_region.child_region(Rect::from_min_size( let mut content_ui = outer_ui.child_ui(Rect::from_min_size(
outer_region.cursor() - state.offset, outer_ui.cursor() - state.offset,
vec2(inner_size.x, f32::INFINITY), vec2(inner_size.x, f32::INFINITY),
)); ));
let mut content_clip_rect = outer_region.clip_rect().intersect(inner_rect); let mut content_clip_rect = outer_ui.clip_rect().intersect(inner_rect);
content_clip_rect.max.x = outer_region.clip_rect().max.x - current_scroll_bar_width; // Nice handling of forced resizing beyond the possible content_clip_rect.max.x = outer_ui.clip_rect().max.x - current_scroll_bar_width; // Nice handling of forced resizing beyond the possible
content_region.set_clip_rect(content_clip_rect); content_ui.set_clip_rect(content_clip_rect);
add_contents(&mut content_region); add_contents(&mut content_ui);
let content_size = content_region.bounding_size(); let content_size = content_ui.bounding_size();
let inner_rect = Rect::from_min_size( let inner_rect = Rect::from_min_size(
inner_rect.min, inner_rect.min,
@ -100,14 +100,14 @@ impl ScrollArea {
inner_rect.size() + vec2(current_scroll_bar_width, 0.0), inner_rect.size() + vec2(current_scroll_bar_width, 0.0),
); );
let content_interact = outer_region.interact_rect(inner_rect, scroll_area_id.with("area")); let content_interact = outer_ui.interact_rect(inner_rect, scroll_area_id.with("area"));
if content_interact.active { if content_interact.active {
// Dragging scroll area to scroll: // Dragging scroll area to scroll:
state.offset.y -= ctx.input().mouse_move.y; state.offset.y -= ctx.input().mouse_move.y;
} }
// TODO: check that nothing else is being inteacted with // TODO: check that nothing else is being inteacted with
if outer_region.contains_mouse(outer_rect) && ctx.memory().active_id.is_none() { if outer_ui.contains_mouse(outer_rect) && ctx.memory().active_id.is_none() {
state.offset.y -= ctx.input().scroll_delta.y; state.offset.y -= ctx.input().scroll_delta.y;
} }
@ -134,7 +134,7 @@ impl ScrollArea {
// intentionally use same id for inside and outside of handle // intentionally use same id for inside and outside of handle
let interact_id = scroll_area_id.with("vertical"); let interact_id = scroll_area_id.with("vertical");
let handle_interact = outer_region.interact_rect(handle_rect, interact_id); let handle_interact = outer_ui.interact_rect(handle_rect, interact_id);
if let Some(mouse_pos) = ctx.input().mouse_pos { if let Some(mouse_pos) = ctx.input().mouse_pos {
if handle_interact.active { if handle_interact.active {
@ -144,8 +144,7 @@ impl ScrollArea {
} }
} else { } else {
// Check for mouse down outside handle: // Check for mouse down outside handle:
let scroll_bg_interact = let scroll_bg_interact = outer_ui.interact_rect(outer_scroll_rect, interact_id);
outer_region.interact_rect(outer_scroll_rect, interact_id);
if scroll_bg_interact.active { if scroll_bg_interact.active {
// Center scroll at mouse pos: // Center scroll at mouse pos:
@ -164,18 +163,18 @@ impl ScrollArea {
pos2(right, from_content(state.offset.y + inner_rect.height())), pos2(right, from_content(state.offset.y + inner_rect.height())),
); );
let style = outer_region.style(); let style = outer_ui.style();
let handle_fill_color = style.interact_fill_color(&handle_interact); let handle_fill_color = style.interact_fill_color(&handle_interact);
let handle_outline = style.interact_outline(&handle_interact); let handle_outline = style.interact_outline(&handle_interact);
outer_region.add_paint_cmd(PaintCmd::Rect { outer_ui.add_paint_cmd(PaintCmd::Rect {
rect: outer_scroll_rect, rect: outer_scroll_rect,
corner_radius, corner_radius,
fill_color: Some(color::gray(0, 196)), // TODO style fill_color: Some(color::gray(0, 196)), // TODO style
outline: None, outline: None,
}); });
outer_region.add_paint_cmd(PaintCmd::Rect { outer_ui.add_paint_cmd(PaintCmd::Rect {
rect: handle_rect.expand(-2.0), rect: handle_rect.expand(-2.0),
corner_radius, corner_radius,
fill_color: handle_fill_color, fill_color: handle_fill_color,
@ -189,15 +188,12 @@ impl ScrollArea {
// content_size.y.min(inner_rect.size().y), // respect vertical height. // content_size.y.min(inner_rect.size().y), // respect vertical height.
// ); // );
let size = outer_rect.size(); let size = outer_rect.size();
outer_region.reserve_space(size, None); outer_ui.reserve_space(size, None);
state.offset.y = state.offset.y.min(content_size.y - inner_rect.height()); state.offset.y = state.offset.y.min(content_size.y - inner_rect.height());
state.offset.y = state.offset.y.max(0.0); state.offset.y = state.offset.y.max(0.0);
state.show_scroll = show_scroll_this_frame; state.show_scroll = show_scroll_this_frame;
outer_region outer_ui.memory().scroll_areas.insert(scroll_area_id, state);
.memory()
.scroll_areas
.insert(scroll_area_id, state);
} }
} }

View file

@ -85,7 +85,7 @@ impl Window {
} }
impl Window { impl Window {
pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Region)) { pub fn show(self, ctx: &Arc<Context>, add_contents: impl FnOnce(&mut Ui)) {
let Window { let Window {
title_label, title_label,
floating, floating,
@ -96,12 +96,12 @@ impl Window {
frame.margin = Some(frame.margin.unwrap_or_else(|| ctx.style().window_padding)); frame.margin = Some(frame.margin.unwrap_or_else(|| ctx.style().window_padding));
// TODO: easier way to compose these // TODO: easier way to compose these
floating.show(ctx, |region| { floating.show(ctx, |ui| {
frame.show(region, |region| { frame.show(ui, |ui| {
resize.show(region, |region| { resize.show(ui, |ui| {
region.add(title_label); ui.add(title_label);
region.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents ui.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents
scroll.show(region, |region| add_contents(region)) scroll.show(ui, |ui| add_contents(ui))
}) })
}) })
}) })

View file

@ -13,11 +13,11 @@ struct PaintStats {
} }
/// Contains the input, style and output of all GUI commands. /// Contains the input, style and output of all GUI commands.
/// Regions keep an Arc pointer to this. /// `Ui`:s keep an Arc pointer to this.
/// This allows us to create several child regions at once, /// This allows us to create several child `Ui`:s at once,
/// all working against the same shared Context. /// all working against the same shared Context.
pub struct Context { pub struct Context {
/// The default style for new regions /// The default style for new `Ui`:s
style: Mutex<Style>, style: Mutex<Style>,
mesher_options: Mutex<mesher::MesherOptions>, mesher_options: Mutex<mesher::MesherOptions>,
fonts: Arc<Fonts>, fonts: Arc<Fonts>,
@ -207,10 +207,10 @@ impl Context {
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/// A region for the entire screen, behind any windows. /// An ui for the entire screen, behind any windows.
pub fn background_region(self: &Arc<Self>) -> Region { pub fn fullscreen_ui(self: &Arc<Self>) -> Ui {
let rect = Rect::from_min_size(Default::default(), self.input().screen_size); let rect = Rect::from_min_size(Default::default(), self.input().screen_size);
Region::new(self.clone(), Layer::Background, Id::background(), rect) Ui::new(self.clone(), Layer::Background, Id::background(), rect)
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -428,19 +428,19 @@ impl Context {
} }
impl Context { impl Context {
pub fn ui(&self, region: &mut Region) { pub fn ui(&self, ui: &mut Ui) {
use crate::containers::*; use crate::containers::*;
region.collapsing("Style", |region| { ui.collapsing("Style", |ui| {
self.mesher_options.lock().ui(region); self.mesher_options.lock().ui(ui);
self.style_ui(region); self.style_ui(ui);
}); });
region.collapsing("Fonts", |region| { ui.collapsing("Fonts", |ui| {
let old_font_definitions = self.fonts().definitions(); let old_font_definitions = self.fonts().definitions();
let mut new_font_definitions = old_font_definitions.clone(); let mut new_font_definitions = old_font_definitions.clone();
font_definitions_ui(&mut new_font_definitions, region); font_definitions_ui(&mut new_font_definitions, ui);
self.fonts().texture().ui(region); self.fonts().texture().ui(ui);
if *old_font_definitions != new_font_definitions { if *old_font_definitions != new_font_definitions {
let fonts = let fonts =
Fonts::from_definitions(new_font_definitions, self.input().pixels_per_point); Fonts::from_definitions(new_font_definitions, self.input().pixels_per_point);
@ -448,64 +448,62 @@ impl Context {
} }
}); });
region.collapsing("Input", |region| { ui.collapsing("Input", |ui| {
CollapsingHeader::new("Raw Input") CollapsingHeader::new("Raw Input")
.default_open() .default_open()
.show(region, |region| { .show(ui, |ui| ui.ctx().last_raw_input().clone().ui(ui));
region.ctx().last_raw_input().clone().ui(region)
});
CollapsingHeader::new("Input") CollapsingHeader::new("Input")
.default_open() .default_open()
.show(region, |region| region.input().clone().ui(region)); .show(ui, |ui| ui.input().clone().ui(ui));
}); });
region.collapsing("Stats", |region| { ui.collapsing("Stats", |ui| {
region.add(label!( ui.add(label!(
"Screen size: {} x {} points, pixels_per_point: {}", "Screen size: {} x {} points, pixels_per_point: {}",
region.input().screen_size.x, ui.input().screen_size.x,
region.input().screen_size.y, ui.input().screen_size.y,
region.input().pixels_per_point, ui.input().pixels_per_point,
)); ));
if let Some(mouse_pos) = region.input().mouse_pos { if let Some(mouse_pos) = ui.input().mouse_pos {
region.add(label!("mouse_pos: {:.2} x {:.2}", mouse_pos.x, mouse_pos.y,)); ui.add(label!("mouse_pos: {:.2} x {:.2}", mouse_pos.x, mouse_pos.y,));
} else { } else {
region.add_label("mouse_pos: None"); ui.add_label("mouse_pos: None");
} }
region.add(label!("Painting:").text_style(TextStyle::Heading)); ui.add(label!("Painting:").text_style(TextStyle::Heading));
self.paint_stats.lock().ui(region); self.paint_stats.lock().ui(ui);
}); });
} }
} }
fn font_definitions_ui(font_definitions: &mut FontDefinitions, region: &mut Region) { fn font_definitions_ui(font_definitions: &mut FontDefinitions, ui: &mut Ui) {
use crate::widgets::*; use crate::widgets::*;
for (text_style, (_family, size)) in font_definitions.iter_mut() { for (text_style, (_family, size)) in font_definitions.iter_mut() {
// TODO: radiobutton for family // TODO: radiobutton for family
region.add( ui.add(
Slider::f32(size, 4.0..=40.0) Slider::f32(size, 4.0..=40.0)
.precision(0) .precision(0)
.text(format!("{:?}", text_style)), .text(format!("{:?}", text_style)),
); );
} }
if region.add(Button::new("Reset fonts")).clicked { if ui.add(Button::new("Reset fonts")).clicked {
*font_definitions = crate::fonts::default_font_definitions(); *font_definitions = crate::fonts::default_font_definitions();
} }
} }
impl Context { impl Context {
pub fn style_ui(&self, region: &mut Region) { pub fn style_ui(&self, ui: &mut Ui) {
let mut style = self.style(); let mut style = self.style();
style.ui(region); style.ui(ui);
self.set_style(style); self.set_style(style);
} }
} }
impl mesher::MesherOptions { impl mesher::MesherOptions {
pub fn ui(&mut self, region: &mut Region) { pub fn ui(&mut self, ui: &mut Ui) {
use crate::widgets::*; use crate::widgets::*;
region.add(Checkbox::new(&mut self.anti_alias, "Antialias")); ui.add(Checkbox::new(&mut self.anti_alias, "Antialias"));
region.add(Checkbox::new( ui.add(Checkbox::new(
&mut self.debug_paint_clip_rects, &mut self.debug_paint_clip_rects,
"Paint Clip Rects (debug)", "Paint Clip Rects (debug)",
)); ));
@ -513,14 +511,12 @@ impl mesher::MesherOptions {
} }
impl PaintStats { impl PaintStats {
pub fn ui(&self, region: &mut Region) { pub fn ui(&self, ui: &mut Ui) {
region ui.add(label!("Batches: {}", self.num_batches))
.add(label!("Batches: {}", self.num_batches))
.tooltip_text("Number of separate clip rectanlges"); .tooltip_text("Number of separate clip rectanlges");
region ui.add(label!("Primitives: {}", self.num_primitives))
.add(label!("Primitives: {}", self.num_primitives))
.tooltip_text("Boxes, circles, text areas etc"); .tooltip_text("Boxes, circles, text areas etc");
region.add(label!("Vertices: {}", self.num_vertices)); ui.add(label!("Vertices: {}", self.num_vertices));
region.add(label!("Triangles: {}", self.num_triangles)); ui.add(label!("Triangles: {}", self.num_triangles));
} }
} }

View file

@ -2,7 +2,7 @@
use crate::{color::*, containers::*, widgets::*, *}; use crate::{color::*, containers::*, widgets::*, *};
/// Showcase some region code /// Showcase some ui code
pub struct ExampleWindow { pub struct ExampleWindow {
checked: bool, checked: bool,
count: usize, count: usize,
@ -44,73 +44,73 @@ impl Default for ExampleWindow {
} }
impl ExampleWindow { impl ExampleWindow {
pub fn ui(&mut self, region: &mut Region) { pub fn ui(&mut self, ui: &mut Ui) {
region.collapsing("About Emigui", |region| { ui.collapsing("About Emigui", |ui| {
region.add(label!( ui.add(label!(
"Emigui is an experimental immediate mode GUI written in Rust." "Emigui is an experimental immediate mode GUI written in Rust."
)); ));
region.horizontal(|region| { ui.horizontal(|ui| {
region.add_label("Project home page:"); ui.add_label("Project home page:");
region.add_hyperlink("https://github.com/emilk/emigui/"); ui.add_hyperlink("https://github.com/emilk/emigui/");
}); });
}); });
CollapsingHeader::new("Widgets") CollapsingHeader::new("Widgets")
.default_open() .default_open()
.show(region, |region| { .show(ui, |ui| {
region.horizontal(|region| { ui.horizontal(|ui| {
region.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)));
region.add(label!("color").text_color(srgba(128, 140, 255, 255))); ui.add(label!("color").text_color(srgba(128, 140, 255, 255)));
region.add(label!("and tooltips (hover me)")).tooltip_text( ui.add(label!("and tooltips (hover me)")).tooltip_text(
"This is a multiline tooltip that demonstrates that you can easily add tooltips to any element.\nThis is the second line.\nThis is the third.", "This is a multiline tooltip that demonstrates that you can easily add tooltips to any element.\nThis is the second line.\nThis is the third.",
); );
}); });
region.add(Checkbox::new(&mut self.checked, "checkbox")); ui.add(Checkbox::new(&mut self.checked, "checkbox"));
region.horizontal(|region| { ui.horizontal(|ui| {
if region.add(radio(self.radio == 0, "First")).clicked { if ui.add(radio(self.radio == 0, "First")).clicked {
self.radio = 0; self.radio = 0;
} }
if region.add(radio(self.radio == 1, "Second")).clicked { if ui.add(radio(self.radio == 1, "Second")).clicked {
self.radio = 1; self.radio = 1;
} }
if region.add(radio(self.radio == 2, "Final")).clicked { if ui.add(radio(self.radio == 2, "Final")).clicked {
self.radio = 2; self.radio = 2;
} }
}); });
region.horizontal(|region| { ui.horizontal(|ui| {
if region if ui
.add(Button::new("Click me")) .add(Button::new("Click me"))
.tooltip_text("This will just increase a counter.") .tooltip_text("This will just increase a counter.")
.clicked .clicked
{ {
self.count += 1; self.count += 1;
} }
region.add(label!( ui.add(label!(
"The button has been clicked {} times", "The button has been clicked {} times",
self.count self.count
)); ));
}); });
region.add(Slider::usize(&mut self.slider_value, 1..=1000).text("value")); ui.add(Slider::usize(&mut self.slider_value, 1..=1000).text("value"));
if region.add(Button::new("Double it")).clicked { if ui.add(Button::new("Double it")).clicked {
self.slider_value *= 2; self.slider_value *= 2;
} }
for (i, text) in self.text_inputs.iter_mut().enumerate() { for (i, text) in self.text_inputs.iter_mut().enumerate() {
region.horizontal(|region|{ ui.horizontal(|ui|{
region.add(label!("Text input {}: ", i)); ui.add(label!("Text input {}: ", i));
region.add(TextEdit::new(text).id(i)); ui.add(TextEdit::new(text).id(i));
}); // TODO: .tooltip_text("Enter text to edit me") }); // TODO: .tooltip_text("Enter text to edit me")
} }
}); });
region.collapsing("Layouts", |region| { ui.collapsing("Layouts", |ui| {
region.add(Slider::usize(&mut self.num_columns, 1..=10).text("Columns")); ui.add(Slider::usize(&mut self.num_columns, 1..=10).text("Columns"));
region.columns(self.num_columns, |cols| { ui.columns(self.num_columns, |cols| {
for (i, col) in cols.iter_mut().enumerate() { for (i, col) in cols.iter_mut().enumerate() {
col.add(label!("Column {} out of {}", i + 1, self.num_columns)); col.add(label!("Column {} out of {}", i + 1, self.num_columns));
if i + 1 == self.num_columns && col.add(Button::new("Delete this")).clicked { if i + 1 == self.num_columns && col.add(Button::new("Delete this")).clicked {
@ -120,14 +120,14 @@ impl ExampleWindow {
}); });
}); });
region.collapsing("Test box rendering", |region| { ui.collapsing("Test box rendering", |ui| {
region.add(Slider::f32(&mut self.size.x, 0.0..=500.0).text("width")); ui.add(Slider::f32(&mut self.size.x, 0.0..=500.0).text("width"));
region.add(Slider::f32(&mut self.size.y, 0.0..=500.0).text("height")); ui.add(Slider::f32(&mut self.size.y, 0.0..=500.0).text("height"));
region.add(Slider::f32(&mut self.corner_radius, 0.0..=50.0).text("corner_radius")); ui.add(Slider::f32(&mut self.corner_radius, 0.0..=50.0).text("corner_radius"));
region.add(Slider::f32(&mut self.stroke_width, 0.0..=10.0).text("stroke_width")); ui.add(Slider::f32(&mut self.stroke_width, 0.0..=10.0).text("stroke_width"));
region.add(Slider::usize(&mut self.num_boxes, 0..=5).text("num_boxes")); ui.add(Slider::usize(&mut self.num_boxes, 0..=5).text("num_boxes"));
let pos = region let pos = ui
.reserve_space( .reserve_space(
vec2(self.size.x * (self.num_boxes as f32), self.size.y), vec2(self.size.x * (self.num_boxes as f32), self.size.y),
None, None,
@ -147,56 +147,56 @@ impl ExampleWindow {
outline: Some(Outline::new(self.stroke_width, gray(255, 255))), outline: Some(Outline::new(self.stroke_width, gray(255, 255))),
}); });
} }
region.add_paint_cmds(cmds); ui.add_paint_cmds(cmds);
}); });
CollapsingHeader::new("Scroll area") CollapsingHeader::new("Scroll area")
// .default_open() // .default_open()
.show(region, |region| { .show(ui, |ui| {
ScrollArea::default().show(region, |region| { ScrollArea::default().show(ui, |ui| {
region.add_label(LOREM_IPSUM); ui.add_label(LOREM_IPSUM);
}); });
}); });
CollapsingHeader::new("Painting") CollapsingHeader::new("Painting")
// .default_open() // .default_open()
.show(region, |region| self.painting.ui(region)); .show(ui, |ui| self.painting.ui(ui));
CollapsingHeader::new("Resize") CollapsingHeader::new("Resize")
// .default_open() // .default_open()
.show(region, |region| { .show(ui, |ui| {
Resize::default() Resize::default()
.default_height(200.0) .default_height(200.0)
// .as_wide_as_possible() // .as_wide_as_possible()
.auto_shrink_height(false) .auto_shrink_height(false)
.show(region, |region| { .show(ui, |ui| {
region.add(label!("This region can be resized!")); ui.add(label!("This ui can be resized!"));
region.add(label!("Just pull the handle on the bottom right")); ui.add(label!("Just pull the handle on the bottom right"));
}); });
}); });
region.collapsing("Name clash example", |region| { ui.collapsing("Name clash example", |ui| {
region.add_label("\ ui.add_label("\
Regions that store state require unique identifiers so we can track their state between frames. \ Widgets that store state require unique identifiers so we can track their state between frames. \
Identifiers are normally derived from the titles of the widget."); Identifiers are normally derived from the titles of the widget.");
region.add_label("\ ui.add_label("\
For instance, collapsing regions needs to store wether or not they are open. \ For instance, collapsable headers needs to store wether or not they are open. \
If you fail to give them unique names then clicking one will open both. \ If you fail to give them unique names then clicking one will open both. \
To help you debug this, an error message is printed on screen:"); To help you debug this, an error message is printed on screen:");
region.collapsing("Collapsing header", |region| { ui.collapsing("Collapsing header", |ui| {
region.add_label("Contents of first folddable region"); ui.add_label("Contents of first folddable ui");
}); });
region.collapsing("Collapsing header", |region| { ui.collapsing("Collapsing header", |ui| {
region.add_label("Contents of second folddable region"); ui.add_label("Contents of second folddable ui");
}); });
region.add_label("\ ui.add_label("\
Most widgets don't need unique names, but are tracked \ Most widgets don't need unique names, but are tracked \
based on their position on screen. For instance, buttons:"); based on their position on screen. For instance, buttons:");
region.add(Button::new("Button")); ui.add(Button::new("Button"));
region.add(Button::new("Button")); ui.add(Button::new("Button"));
}); });
} }
} }
@ -207,16 +207,16 @@ struct Painting {
} }
impl Painting { impl Painting {
pub fn ui(&mut self, region: &mut Region) { pub fn ui(&mut self, ui: &mut Ui) {
region.add_label("Draw with your mouse to paint"); ui.add_label("Draw with your mouse to paint");
if region.add(Button::new("Clear")).clicked { if ui.add(Button::new("Clear")).clicked {
self.lines.clear(); self.lines.clear();
} }
region.add_custom_contents(vec2(f32::INFINITY, 200.0), |region| { ui.add_custom_contents(vec2(f32::INFINITY, 200.0), |ui| {
let canvas_corner = region.cursor(); let canvas_corner = ui.cursor();
let interact = region.reserve_space(region.available_space(), Some(region.id())); let interact = ui.reserve_space(ui.available_space(), Some(ui.id()));
region.set_clip_rect(region.clip_rect().intersect(interact.rect)); // Make sure we don't paint out of bounds ui.set_clip_rect(ui.clip_rect().intersect(interact.rect)); // Make sure we don't paint out of bounds
if self.lines.is_empty() { if self.lines.is_empty() {
self.lines.push(vec![]); self.lines.push(vec![]);
@ -225,7 +225,7 @@ impl Painting {
let current_line = self.lines.last_mut().unwrap(); let current_line = self.lines.last_mut().unwrap();
if interact.active { if interact.active {
if let Some(mouse_pos) = region.input().mouse_pos { if let Some(mouse_pos) = ui.input().mouse_pos {
let canvas_pos = mouse_pos - canvas_corner; let canvas_pos = mouse_pos - canvas_corner;
if current_line.last() != Some(&canvas_pos) { if current_line.last() != Some(&canvas_pos) {
current_line.push(canvas_pos); current_line.push(canvas_pos);
@ -237,7 +237,7 @@ impl Painting {
for line in &self.lines { for line in &self.lines {
if line.len() >= 2 { if line.len() >= 2 {
region.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::Line {
points: line.iter().map(|p| canvas_corner + *p).collect(), points: line.iter().map(|p| canvas_corner + *p).collect(),
color: LIGHT_GRAY, color: LIGHT_GRAY,
width: 2.0, width: 2.0,
@ -246,8 +246,8 @@ impl Painting {
} }
// Frame it: // Frame it:
region.add_paint_cmd(PaintCmd::Rect { ui.add_paint_cmd(PaintCmd::Rect {
rect: region.rect(), rect: ui.rect(),
corner_radius: 0.0, corner_radius: 0.0,
fill_color: None, fill_color: None,
outline: Some(Outline::new(1.0, WHITE)), outline: Some(Outline::new(1.0, WHITE)),

View file

@ -17,7 +17,7 @@
//! //!
//! For things that need to persist state even after moving (windows, collapsing headers) //! For things that need to persist state even after moving (windows, collapsing headers)
//! the location of the widgets is obviously not good enough. For instance, //! the location of the widgets is obviously not good enough. For instance,
//! a fodlable region needs to remember wether or not it is open even //! a collapsing region needs to remember wether or not it is open even
//! if the layout next frame is different and the collapsing is not lower down //! if the layout next frame is different and the collapsing is not lower down
//! on the screen. //! on the screen.
//! //!

View file

@ -153,41 +153,41 @@ impl GuiInput {
} }
impl RawInput { impl RawInput {
pub fn ui(&self, region: &mut crate::Region) { pub fn ui(&self, ui: &mut crate::Ui) {
use crate::label; use crate::label;
// TODO: simpler way to show values, e.g. `region.value("Mouse Pos:", self.mouse_pos); // TODO: simpler way to show values, e.g. `ui.value("Mouse Pos:", self.mouse_pos);
// TODO: easily change default font! // TODO: easily change default font!
region.add(label!("mouse_down: {}", self.mouse_down)); ui.add(label!("mouse_down: {}", self.mouse_down));
region.add(label!("mouse_pos: {:.1?}", self.mouse_pos)); ui.add(label!("mouse_pos: {:.1?}", self.mouse_pos));
region.add(label!("scroll_delta: {:?}", self.scroll_delta)); ui.add(label!("scroll_delta: {:?}", self.scroll_delta));
region.add(label!("screen_size: {:?}", self.screen_size)); ui.add(label!("screen_size: {:?}", self.screen_size));
region.add(label!("pixels_per_point: {}", self.pixels_per_point)); ui.add(label!("pixels_per_point: {}", self.pixels_per_point));
region.add(label!("time: {:.3} s", self.time)); ui.add(label!("time: {:.3} s", self.time));
region.add(label!("events: {:?}", self.events)); ui.add(label!("events: {:?}", self.events));
region.add(label!("dropped_files: {:?}", self.dropped_files)); ui.add(label!("dropped_files: {:?}", self.dropped_files));
region.add(label!("hovered_files: {:?}", self.hovered_files)); ui.add(label!("hovered_files: {:?}", self.hovered_files));
} }
} }
impl GuiInput { impl GuiInput {
pub fn ui(&self, region: &mut crate::Region) { pub fn ui(&self, ui: &mut crate::Ui) {
use crate::label; use crate::label;
region.add(label!("mouse_down: {}", self.mouse_down)); ui.add(label!("mouse_down: {}", self.mouse_down));
region.add(label!("mouse_pressed: {}", self.mouse_pressed)); ui.add(label!("mouse_pressed: {}", self.mouse_pressed));
region.add(label!("mouse_released: {}", self.mouse_released)); ui.add(label!("mouse_released: {}", self.mouse_released));
region.add(label!("mouse_pos: {:?}", self.mouse_pos)); ui.add(label!("mouse_pos: {:?}", self.mouse_pos));
region.add(label!("mouse_move: {:?}", self.mouse_move)); ui.add(label!("mouse_move: {:?}", self.mouse_move));
region.add(label!( ui.add(label!(
"mouse_velocity: [{:3.0} {:3.0}] points/sec", "mouse_velocity: [{:3.0} {:3.0}] points/sec",
self.mouse_velocity.x, self.mouse_velocity.x,
self.mouse_velocity.y self.mouse_velocity.y
)); ));
region.add(label!("scroll_delta: {:?}", self.scroll_delta)); ui.add(label!("scroll_delta: {:?}", self.scroll_delta));
region.add(label!("screen_size: {:?}", self.screen_size)); ui.add(label!("screen_size: {:?}", self.screen_size));
region.add(label!("pixels_per_point: {}", self.pixels_per_point)); ui.add(label!("pixels_per_point: {}", self.pixels_per_point));
region.add(label!("time: {:.3} s", self.time)); ui.add(label!("time: {:.3} s", self.time));
region.add(label!("events: {:?}", self.events)); ui.add(label!("events: {:?}", self.events));
region.add(label!("dropped_files: {:?}", self.dropped_files)); ui.add(label!("dropped_files: {:?}", self.dropped_files));
region.add(label!("hovered_files: {:?}", self.hovered_files)); ui.add(label!("hovered_files: {:?}", self.hovered_files));
} }
} }

View file

@ -15,7 +15,7 @@ pub struct GuiResponse {
/// The mouse is interacting with this thing (e.g. dragging it) /// The mouse is interacting with this thing (e.g. dragging it)
pub active: bool, pub active: bool,
/// The region of the screen we are talking about /// The area of the screen we are talking about
pub rect: Rect, pub rect: Rect,
/// Used for optionally showing a tooltip /// Used for optionally showing a tooltip
@ -24,7 +24,7 @@ pub struct GuiResponse {
impl GuiResponse { impl GuiResponse {
/// Show some stuff if the item was hovered /// Show some stuff if the item was hovered
pub fn tooltip(&mut self, add_contents: impl FnOnce(&mut Region)) -> &mut Self { pub fn tooltip(&mut self, add_contents: impl FnOnce(&mut Ui)) -> &mut Self {
if self.hovered { if self.hovered {
if let Some(mouse_pos) = self.ctx.input().mouse_pos { if let Some(mouse_pos) = self.ctx.input().mouse_pos {
let window_pos = mouse_pos + vec2(16.0, 16.0); let window_pos = mouse_pos + vec2(16.0, 16.0);
@ -94,7 +94,7 @@ pub fn align_rect(rect: Rect, align: (Align, Align)) -> Rect {
// TODO: move show_popup, and expand its features (default size, autosize, etc) // TODO: move show_popup, and expand its features (default size, autosize, etc)
/// Show a pop-over window /// Show a pop-over window
pub fn show_popup(ctx: &Arc<Context>, window_pos: Pos2, add_contents: impl FnOnce(&mut Region)) { pub fn show_popup(ctx: &Arc<Context>, window_pos: Pos2, add_contents: impl FnOnce(&mut Ui)) {
let layer = Layer::Popup; let layer = Layer::Popup;
let where_to_put_background = ctx.graphics().layer(layer).len(); let where_to_put_background = ctx.graphics().layer(layer).len();
@ -103,13 +103,13 @@ pub fn show_popup(ctx: &Arc<Context>, window_pos: Pos2, add_contents: impl FnOnc
let size = vec2(ctx.input().screen_size.x.min(350.0), f32::INFINITY); // TODO: popup/tooltip width let size = vec2(ctx.input().screen_size.x.min(350.0), f32::INFINITY); // TODO: popup/tooltip width
let inner_rect = Rect::from_min_size(window_pos + window_padding, size); let inner_rect = Rect::from_min_size(window_pos + window_padding, size);
let mut contents_region = Region::new(ctx.clone(), layer, Id::popup(), inner_rect); let mut contents_ui = Ui::new(ctx.clone(), layer, Id::popup(), inner_rect);
add_contents(&mut contents_region); add_contents(&mut contents_ui);
// Now insert popup background: // Now insert popup background:
let inner_size = contents_region.bounding_size(); let inner_size = contents_ui.bounding_size();
let outer_size = inner_size + 2.0 * window_padding; let outer_size = inner_size + 2.0 * window_padding;
let rect = Rect::from_min_size(window_pos, outer_size); let rect = Rect::from_min_size(window_pos, outer_size);

View file

@ -36,10 +36,10 @@ pub mod math;
mod memory; mod memory;
pub mod mesher; pub mod mesher;
mod movement_tracker; mod movement_tracker;
mod region;
mod style; mod style;
mod texture_atlas; mod texture_atlas;
mod types; mod types;
mod ui;
pub mod widgets; pub mod widgets;
pub use { pub use {
@ -54,9 +54,9 @@ pub use {
memory::Memory, memory::Memory,
mesher::{Mesh, PaintBatches, Vertex}, mesher::{Mesh, PaintBatches, Vertex},
movement_tracker::MovementTracker, movement_tracker::MovementTracker,
region::Region,
style::Style, style::Style,
texture_atlas::Texture, texture_atlas::Texture,
types::*, types::*,
ui::Ui,
widgets::Widget, widgets::Widget,
}; };

View file

@ -44,7 +44,7 @@ pub struct Style {
// ----------------------------------------------- // -----------------------------------------------
// Debug rendering: // Debug rendering:
pub debug_regions: bool, pub debug_uis: bool,
} }
#[derive(Clone, Copy, Debug, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
@ -67,7 +67,7 @@ impl Default for Style {
animation_time: 1.0 / 20.0, animation_time: 1.0 / 20.0,
window: Window::default(), window: Window::default(),
clip_rect_margin: 3.0, clip_rect_margin: 3.0,
debug_regions: false, debug_uis: false,
} }
} }
} }
@ -164,33 +164,33 @@ impl Style {
impl Style { impl Style {
#[rustfmt::skip] #[rustfmt::skip]
pub fn ui(&mut self, region: &mut crate::Region) { pub fn ui(&mut self, ui: &mut crate::Ui) {
use crate::{widgets::*, *}; use crate::{widgets::*, *};
if region.add(Button::new("Reset style")).clicked { if ui.add(Button::new("Reset style")).clicked {
*self = Default::default(); *self = Default::default();
} }
region.add(label!("Debug:").text_style(TextStyle::Heading)); ui.add(label!("Debug:").text_style(TextStyle::Heading));
region.add(Checkbox::new(&mut self.debug_regions, "debug_regions")); ui.add(Checkbox::new(&mut self.debug_uis, "debug_uis"));
region.add(Separator::new()); ui.add(Separator::new());
// TODO: region.section("Heading", |ui| ui.add(contents)) // TODO: ui.section("Heading", |ui| ui.add(contents))
region.add(Slider::f32(&mut self.item_spacing.x, 0.0..=10.0).text("item_spacing.x").precision(0)); ui.add(Slider::f32(&mut self.item_spacing.x, 0.0..=10.0).text("item_spacing.x").precision(0));
region.add(Slider::f32(&mut self.item_spacing.y, 0.0..=10.0).text("item_spacing.y").precision(0)); ui.add(Slider::f32(&mut self.item_spacing.y, 0.0..=10.0).text("item_spacing.y").precision(0));
region.add(Slider::f32(&mut self.window_padding.x, 0.0..=10.0).text("window_padding.x").precision(0)); ui.add(Slider::f32(&mut self.window_padding.x, 0.0..=10.0).text("window_padding.x").precision(0));
region.add(Slider::f32(&mut self.window_padding.y, 0.0..=10.0).text("window_padding.y").precision(0)); ui.add(Slider::f32(&mut self.window_padding.y, 0.0..=10.0).text("window_padding.y").precision(0));
region.add(Slider::f32(&mut self.indent, 0.0..=100.0).text("indent").precision(0)); ui.add(Slider::f32(&mut self.indent, 0.0..=100.0).text("indent").precision(0));
region.add(Slider::f32(&mut self.button_padding.x, 0.0..=20.0).text("button_padding.x").precision(0)); ui.add(Slider::f32(&mut self.button_padding.x, 0.0..=20.0).text("button_padding.x").precision(0));
region.add(Slider::f32(&mut self.button_padding.y, 0.0..=20.0).text("button_padding.y").precision(0)); ui.add(Slider::f32(&mut self.button_padding.y, 0.0..=20.0).text("button_padding.y").precision(0));
region.add(Slider::f32(&mut self.clickable_diameter, 0.0..=60.0).text("clickable_diameter").precision(0)); ui.add(Slider::f32(&mut self.clickable_diameter, 0.0..=60.0).text("clickable_diameter").precision(0));
region.add(Slider::f32(&mut self.start_icon_width, 0.0..=60.0).text("start_icon_width").precision(0)); ui.add(Slider::f32(&mut self.start_icon_width, 0.0..=60.0).text("start_icon_width").precision(0));
region.add(Slider::f32(&mut self.line_width, 0.0..=10.0).text("line_width").precision(0)); ui.add(Slider::f32(&mut self.line_width, 0.0..=10.0).text("line_width").precision(0));
region.add(Slider::f32(&mut self.animation_time, 0.0..=1.0).text("animation_time").precision(2)); ui.add(Slider::f32(&mut self.animation_time, 0.0..=1.0).text("animation_time").precision(2));
// TODO: region.section("Heading", |ui| ui.add(contents)) // TODO: ui.section("Heading", |ui| ui.add(contents))
region.add(Separator::new()); ui.add(Separator::new());
region.add(label!("Debug:").text_style(TextStyle::Heading)); ui.add(label!("Debug:").text_style(TextStyle::Heading));
region.add(Checkbox::new(&mut self.debug_regions, "debug_regions")); ui.add(Checkbox::new(&mut self.debug_uis, "debug_uis"));
} }
} }

View file

@ -91,19 +91,19 @@ impl TextureAtlas {
} }
impl Texture { impl Texture {
pub fn ui(&self, region: &mut crate::Region) { pub fn ui(&self, ui: &mut crate::Ui) {
use crate::{color::WHITE, label, layout::show_popup, math::*, Mesh, PaintCmd, Vertex}; use crate::{color::WHITE, label, layout::show_popup, math::*, Mesh, PaintCmd, Vertex};
region.add(label!( ui.add(label!(
"Texture size: {} x {} (hover to zoom)", "Texture size: {} x {} (hover to zoom)",
self.width, self.width,
self.height self.height
)); ));
let mut size = vec2(self.width as f32, self.height as f32); let mut size = vec2(self.width as f32, self.height as f32);
if size.x > region.available_width() { if size.x > ui.available_width() {
size *= region.available_width() / size.x; size *= ui.available_width() / size.x;
} }
let interact = region.reserve_space(size, None); let interact = ui.reserve_space(size, None);
let rect = interact.rect; let rect = interact.rect;
let top_left = Vertex { let top_left = Vertex {
pos: rect.min, pos: rect.min,
@ -117,12 +117,12 @@ impl Texture {
}; };
let mut mesh = Mesh::default(); let mut mesh = Mesh::default();
mesh.add_rect(top_left, bottom_right); mesh.add_rect(top_left, bottom_right);
region.add_paint_cmd(PaintCmd::Mesh(mesh)); ui.add_paint_cmd(PaintCmd::Mesh(mesh));
if let Some(mouse_pos) = region.input().mouse_pos { if let Some(mouse_pos) = ui.input().mouse_pos {
if interact.hovered { if interact.hovered {
show_popup(region.ctx(), mouse_pos, |region| { show_popup(ui.ctx(), mouse_pos, |ui| {
let zoom_rect = region.reserve_space(vec2(128.0, 128.0), None).rect; let zoom_rect = ui.reserve_space(vec2(128.0, 128.0), None).rect;
let u = remap_clamp(mouse_pos.x, rect.range_x(), 0.0..=self.width as f32 - 1.0) let u = remap_clamp(mouse_pos.x, rect.range_x(), 0.0..=self.width as f32 - 1.0)
.round(); .round();
let v = let v =
@ -145,7 +145,7 @@ impl Texture {
}; };
let mut mesh = Mesh::default(); let mut mesh = Mesh::default();
mesh.add_rect(top_left, bottom_right); mesh.add_rect(top_left, bottom_right);
region.add_paint_cmd(PaintCmd::Mesh(mesh)); ui.add_paint_cmd(PaintCmd::Mesh(mesh));
}); });
} }
} }

View file

@ -4,40 +4,39 @@ use crate::{color::*, containers::*, font::TextFragment, layout::*, widgets::*,
/// Represents a region of the screen /// Represents a region of the screen
/// with a type of layout (horizontal or vertical). /// with a type of layout (horizontal or vertical).
/// TODO: make Region a trait so we can have type-safe `HorizontalRegion` etc? pub struct Ui {
pub struct Region {
/// How we access input, output and memory /// How we access input, output and memory
ctx: Arc<Context>, ctx: Arc<Context>,
/// ID of this region. /// ID of this ui.
/// Generated based on id of parent region together with /// Generated based on id of parent ui together with
/// another source of child identity (e.g. window title). /// another source of child identity (e.g. window title).
/// Acts like a namespace for child regions. /// Acts like a namespace for child uis.
/// Hopefully unique. /// Hopefully unique.
id: Id, id: Id,
/// Where to put the graphics output of this Region /// Where to put the graphics output of this Ui
layer: Layer, layer: Layer,
/// Everything painted in this region will be clipped against this. /// Everything painted in this ui will be clipped against this.
/// This means nothing outside of this rectangle will be visible on screen. /// This means nothing outside of this rectangle will be visible on screen.
clip_rect: Rect, clip_rect: Rect,
/// The `rect` represents where in space the region is /// The `rect` represents where in screen-space the ui is
/// and its max size (original available_space). /// and its max size (original available_space).
/// Note that the size may be infinite in one or both dimensions. /// Note that the size may be infinite in one or both dimensions.
/// The widgets will TRY to fit within the rect, /// The widgets will TRY to fit within the rect,
/// but may overflow (which you will see in child_bounds). /// but may overflow (which you will see in child_bounds).
/// Some widgets (like separator lines) will try to fill the full desired width of the region. /// Some widgets (like separator lines) will try to fill the full desired width of the ui.
desired_rect: Rect, // TODO: rename as max_rect ? desired_rect: Rect, // TODO: rename as max_rect ?
/// Bounding box of all children. /// Bounding box of all children.
/// This is used to see how large a region actually /// This is used to see how large a ui actually
/// needs to be after all children has been added. /// needs to be after all children has been added.
/// You can think of this as the minimum size. /// You can think of this as the minimum size.
child_bounds: Rect, // TODO: rename as min_rect ? child_bounds: Rect, // TODO: rename as min_rect ?
/// Overide default style in this region /// Overide default style in this ui
style: Style, style: Style,
// Layout stuff follows. TODO: move to own type and abstract. // Layout stuff follows. TODO: move to own type and abstract.
@ -54,13 +53,13 @@ pub struct Region {
cursor: Pos2, cursor: Pos2,
} }
impl Region { impl Ui {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Creation: // Creation:
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();
Region { Ui {
ctx, ctx,
id, id,
layer, layer,
@ -74,12 +73,12 @@ impl Region {
} }
} }
pub fn child_region(&self, child_rect: Rect) -> Self { pub fn child_ui(&self, child_rect: Rect) -> Self {
// let clip_rect = self // let clip_rect = self
// .clip_rect // .clip_rect
// .intersect(&child_rect.expand(self.style().clip_rect_margin)); // .intersect(&child_rect.expand(self.style().clip_rect_margin));
let clip_rect = self.clip_rect(); // Keep it unless the child explciitly desires differently let clip_rect = self.clip_rect(); // Keep it unless the child explciitly desires differently
Region { Ui {
ctx: self.ctx.clone(), ctx: self.ctx.clone(),
layer: self.layer, layer: self.layer,
style: self.style, style: self.style,
@ -111,7 +110,7 @@ impl Region {
self.id self.id
} }
/// Options for this region, and any child regions we may spawn. /// Options for this ui, and any child uis we may spawn.
pub fn style(&self) -> &Style { pub fn style(&self) -> &Style {
&self.style &self.style
} }
@ -136,7 +135,7 @@ impl Region {
self.ctx.fonts() self.ctx.fonts()
} }
/// Screen-space rectangle for clipping what we paint in this region. /// Screen-space rectangle for clipping what we paint in this ui.
/// This is used, for instance, to avoid painting outside a window that is smaller /// This is used, for instance, to avoid painting outside a window that is smaller
/// than its contents. /// than its contents.
pub fn clip_rect(&self) -> Rect { pub fn clip_rect(&self) -> Rect {
@ -149,28 +148,28 @@ impl Region {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/// Screen-space position of this Region. /// Screen-space position of this Ui.
/// This may have moved from its original if a child overflowed to the left or up (rare). /// This may have moved from its original if a child overflowed to the left or up (rare).
pub fn top_left(&self) -> Pos2 { pub fn top_left(&self) -> Pos2 {
// If a child doesn't fit in desired_rect, we have effectively expanded: // If a child doesn't fit in desired_rect, we have effectively expanded:
self.desired_rect.min.min(self.child_bounds.min) self.desired_rect.min.min(self.child_bounds.min)
} }
/// Screen-space position of the current bottom right corner of this Region. /// Screen-space position of the current bottom right corner of this Ui.
/// This may move when we add children that overflow our desired rectangle bounds. /// This may move when we add children that overflow our desired rectangle bounds.
pub fn bottom_right(&self) -> Pos2 { pub fn bottom_right(&self) -> Pos2 {
// If a child doesn't fit in desired_rect, we have effectively expanded: // If a child doesn't fit in desired_rect, we have effectively expanded:
self.desired_rect.max.max(self.child_bounds.max) self.desired_rect.max.max(self.child_bounds.max)
} }
/// Position and current size of the region. /// Position and current size of the ui.
/// The size is the maximum of the origional (minimum/desired) size and /// The size is the maximum of the origional (minimum/desired) size and
/// the size of the containted children. /// the size of the containted children.
pub fn rect(&self) -> Rect { pub fn rect(&self) -> Rect {
Rect::from_min_max(self.top_left(), self.bottom_right()) Rect::from_min_max(self.top_left(), self.bottom_right())
} }
/// Set the width of the region. /// Set the width of the ui.
/// You won't be able to shrink it beyond its current child bounds. /// You won't be able to shrink it beyond its current child bounds.
pub fn set_desired_width(&mut self, width: f32) { pub fn set_desired_width(&mut self, width: f32) {
let min_width = self.child_bounds.max.x - self.top_left().x; let min_width = self.child_bounds.max.x - self.top_left().x;
@ -178,7 +177,7 @@ impl Region {
self.desired_rect.max.x = self.top_left().x + width; self.desired_rect.max.x = self.top_left().x + width;
} }
/// Set the height of the region. /// Set the height of the ui.
/// You won't be able to shrink it beyond its current child bounds. /// You won't be able to shrink it beyond its current child bounds.
pub fn set_desired_height(&mut self, height: f32) { pub fn set_desired_height(&mut self, height: f32) {
let min_height = self.child_bounds.max.y - self.top_left().y; let min_height = self.child_bounds.max.y - self.top_left().y;
@ -191,7 +190,7 @@ impl Region {
self.child_bounds.max - self.top_left() self.child_bounds.max - self.top_left()
} }
/// Expand the bounding rect of this region to include a child at the given rect. /// Expand the bounding rect of this ui to include a child at the given rect.
pub fn expand_to_include_child(&mut self, rect: Rect) { pub fn expand_to_include_child(&mut self, rect: Rect) {
self.child_bounds.extend_with(rect.min); self.child_bounds.extend_with(rect.min);
self.child_bounds.extend_with(rect.max); self.child_bounds.extend_with(rect.max);
@ -257,7 +256,7 @@ impl Region {
/// Will warn if the returned id is not guaranteed unique. /// Will warn if the returned id is not guaranteed unique.
/// Use this to generate widget ids for widgets that have persistent state in Memory. /// Use this to generate widget ids for widgets that have persistent state in Memory.
/// If the `id_source` is not unique within this region /// If the `id_source` is not unique within this ui
/// then an error will be printed at the current cursor position. /// then an error will be printed at the current cursor position.
pub fn make_unique_id<IdSource>(&self, id_source: &IdSource) -> Id pub fn make_unique_id<IdSource>(&self, id_source: &IdSource) -> Id
where where
@ -282,7 +281,7 @@ impl Region {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Interaction // Interaction
/// Check for clicks on this entire region (rect()) /// Check for clicks on this entire ui (rect())
pub fn interact_whole(&self) -> InteractInfo { pub fn interact_whole(&self) -> InteractInfo {
self.interact_rect(self.rect(), self.id) self.interact_rect(self.rect(), self.id)
} }
@ -304,7 +303,7 @@ impl Region {
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Stuff that moves the cursor, i.e. allocates space in this region! // Stuff that moves the cursor, i.e. allocates space in this ui!
/// Reserve this much space and move the cursor. /// Reserve this much space and move the cursor.
/// Returns where to put the widget. /// Returns where to put the widget.
@ -324,7 +323,7 @@ impl Region {
let child_pos = self.reserve_space_impl(child_size); let child_pos = self.reserve_space_impl(child_size);
let rect = Rect::from_min_size(child_pos, child_size); let rect = Rect::from_min_size(child_pos, child_size);
if self.style().debug_regions { if self.style().debug_uis {
self.add_paint_cmd(PaintCmd::Rect { self.add_paint_cmd(PaintCmd::Rect {
rect, rect,
corner_radius: 0.0, corner_radius: 0.0,
@ -446,7 +445,7 @@ impl Region {
self.ctx.debug_text(pos, text); self.ctx.debug_text(pos, text);
} }
/// Show some text anywhere in the region. /// Show some text anywhere in the ui.
/// To center the text at the given position, use `align: (Center, Center)`. /// To center the text at the given position, use `align: (Center, Center)`.
/// If you want to draw text floating on top of everything, /// If you want to draw text floating on top of everything,
/// consider using `Context.floating_text` instead. /// consider using `Context.floating_text` instead.
@ -503,38 +502,38 @@ impl Region {
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Addding Containers / Sub-Regions: // Addding Containers / Sub-uis:
pub fn collapsing( pub fn collapsing(
&mut self, &mut self,
text: impl Into<String>, text: impl Into<String>,
add_contents: impl FnOnce(&mut Region), add_contents: impl FnOnce(&mut Ui),
) -> GuiResponse { ) -> GuiResponse {
CollapsingHeader::new(text).show(self, add_contents) CollapsingHeader::new(text).show(self, add_contents)
} }
/// Create a child region at the current cursor. /// Create a child ui at the current cursor.
/// `size` is the desired size. /// `size` is the desired size.
/// Actual size may be much smaller if `avilable_size()` is not enough. /// Actual size may be much smaller if `avilable_size()` is not enough.
/// Set `size` to `Vec::infinity()` to get as much space as possible. /// Set `size` to `Vec::infinity()` to get as much space as possible.
/// Just because you ask for a lot of space does not mean you have to use it! /// Just because you ask for a lot of space does not mean you have to use it!
/// After `add_contents` is called the contents of `bounding_size` /// After `add_contents` is called the contents of `bounding_size`
/// will decide how much space will be used in the parent region. /// will decide how much space will be used in the parent ui.
pub fn add_custom_contents(&mut self, size: Vec2, add_contents: impl FnOnce(&mut Region)) { pub fn add_custom_contents(&mut self, size: Vec2, add_contents: impl FnOnce(&mut Ui)) {
let size = size.min(self.available_space()); let size = size.min(self.available_space());
let child_rect = Rect::from_min_size(self.cursor, size); let child_rect = Rect::from_min_size(self.cursor, size);
let mut child_region = Region { let mut child_ui = Ui {
..self.child_region(child_rect) ..self.child_ui(child_rect)
}; };
add_contents(&mut child_region); add_contents(&mut child_ui);
self.reserve_space(child_region.bounding_size(), None); self.reserve_space(child_ui.bounding_size(), None);
} }
/// Create a child region which is indented to the right /// Create a child ui which is indented to the right
pub fn indent( pub fn indent(
&mut self, &mut self,
id_source: impl Hash, id_source: impl Hash,
add_contents: impl FnOnce(&mut Region), add_contents: impl FnOnce(&mut Ui),
) -> InteractInfo { ) -> InteractInfo {
assert!( assert!(
self.dir == Direction::Vertical, self.dir == Direction::Vertical,
@ -542,15 +541,15 @@ impl Region {
); );
let indent = vec2(self.style.indent, 0.0); let indent = vec2(self.style.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_region = Region { let mut child_ui = Ui {
id: self.id.with(id_source), id: self.id.with(id_source),
align: Align::Min, align: Align::Min,
..self.child_region(child_rect) ..self.child_ui(child_rect)
}; };
add_contents(&mut child_region); add_contents(&mut child_ui);
let size = child_region.bounding_size(); let size = child_ui.bounding_size();
// draw a grey line on the left to mark the region // draw a grey line on the left to mark the indented section
let line_start = child_rect.min - indent * 0.5; let line_start = child_rect.min - indent * 0.5;
let line_start = line_start.round(); // TODO: round to pixel instead let line_start = line_start.round(); // TODO: round to pixel instead
let line_end = pos2(line_start.x, line_start.y + size.y - 8.0); let line_end = pos2(line_start.x, line_start.y + size.y - 8.0);
@ -563,38 +562,38 @@ impl Region {
self.reserve_space(indent + size, None) self.reserve_space(indent + size, None)
} }
pub fn left_column(&mut self, width: f32) -> Region { pub fn left_column(&mut self, width: f32) -> Ui {
self.column(Align::Min, width) self.column(Align::Min, width)
} }
pub fn centered_column(&mut self, width: f32) -> Region { pub fn centered_column(&mut self, width: f32) -> Ui {
self.column(Align::Center, width) self.column(Align::Center, width)
} }
pub fn right_column(&mut self, width: f32) -> Region { pub fn right_column(&mut self, width: f32) -> Ui {
self.column(Align::Max, width) self.column(Align::Max, width)
} }
/// A column region with a given width. /// A column ui with a given width.
pub fn column(&mut self, column_position: Align, width: f32) -> Region { pub fn column(&mut self, column_position: Align, width: f32) -> Ui {
let x = match column_position { let x = match column_position {
Align::Min => 0.0, Align::Min => 0.0,
Align::Center => self.available_width() / 2.0 - width / 2.0, Align::Center => self.available_width() / 2.0 - width / 2.0,
Align::Max => self.available_width() - width, Align::Max => self.available_width() - width,
}; };
self.child_region(Rect::from_min_size( self.child_ui(Rect::from_min_size(
self.cursor + vec2(x, 0.0), self.cursor + vec2(x, 0.0),
vec2(width, self.available_height()), vec2(width, self.available_height()),
)) ))
} }
/// Start a region with horizontal layout /// Start a ui with horizontal layout
pub fn horizontal(&mut self, add_contents: impl FnOnce(&mut Region)) { pub fn horizontal(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self.inner_layout(Direction::Horizontal, Align::Min, add_contents) self.inner_layout(Direction::Horizontal, Align::Min, add_contents)
} }
/// Start a region with vertical layout /// Start a ui with vertical layout
pub fn vertical(&mut self, add_contents: impl FnOnce(&mut Region)) { pub fn vertical(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self.inner_layout(Direction::Vertical, Align::Min, add_contents) self.inner_layout(Direction::Vertical, Align::Min, add_contents)
} }
@ -605,20 +604,20 @@ impl Region {
add_contents: impl FnOnce(&mut Self), add_contents: impl FnOnce(&mut Self),
) { ) {
let child_rect = Rect::from_min_max(self.cursor, self.bottom_right()); let child_rect = Rect::from_min_max(self.cursor, self.bottom_right());
let mut child_region = Self { let mut child_ui = Self {
dir, dir,
align, align,
..self.child_region(child_rect) ..self.child_ui(child_rect)
}; };
add_contents(&mut child_region); add_contents(&mut child_ui);
let size = child_region.bounding_size(); let size = child_ui.bounding_size();
self.reserve_space(size, None); self.reserve_space(size, None);
} }
/// Temporarily split split a vertical layout into several columns. /// Temporarily split split a vertical layout into several columns.
/// ///
/// ``` ignore /// ``` ignore
/// region.columns(2, |columns| { /// ui.columns(2, |columns| {
/// columns[0].add(emigui::widgets::label!("First column")); /// columns[0].add(emigui::widgets::label!("First column"));
/// columns[1].add(emigui::widgets::label!("Second column")); /// columns[1].add(emigui::widgets::label!("Second column"));
/// }); /// });
@ -641,7 +640,7 @@ impl Region {
Self { Self {
id: self.make_child_id(&("column", col_idx)), id: self.make_child_id(&("column", col_idx)),
dir: Direction::Vertical, dir: Direction::Vertical,
..self.child_region(child_rect) ..self.child_ui(child_rect)
} }
}) })
.collect(); .collect();
@ -654,8 +653,8 @@ impl Region {
} }
let mut max_height = 0.0; let mut max_height = 0.0;
for region in columns { for ui in columns {
let size = region.bounding_size(); let size = ui.bounding_size();
max_height = size.y.max(max_height); max_height = size.y.max(max_height);
} }

View file

@ -12,9 +12,9 @@ pub use {slider::*, text_edit::*};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// Anything implementing Widget can be added to a Region with `Region::add` /// Anything implementing Widget can be added to a Ui with `Ui::add`
pub trait Widget { pub trait Widget {
fn ui(self, region: &mut Region) -> GuiResponse; fn ui(self, ui: &mut Ui) -> GuiResponse;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -23,7 +23,7 @@ pub struct Label {
// TODO: not pub // TODO: not pub
pub(crate) text: String, pub(crate) text: String,
pub(crate) multiline: bool, pub(crate) multiline: bool,
pub(crate) text_style: TextStyle, // TODO: Option<TextStyle>, where None means "use the default for the region" pub(crate) text_style: TextStyle, // TODO: Option<TextStyle>, where None means "use the default for the ui"
pub(crate) text_color: Option<Color>, pub(crate) text_color: Option<Color>,
} }
@ -52,9 +52,9 @@ impl Label {
self self
} }
pub fn layout(&self, pos: Pos2, region: &Region) -> (Vec<font::TextFragment>, Vec2) { pub fn layout(&self, pos: Pos2, ui: &Ui) -> (Vec<font::TextFragment>, Vec2) {
let font = &region.fonts()[self.text_style]; let font = &ui.fonts()[self.text_style];
let max_width = region.rect().right() - pos.x; let max_width = ui.rect().right() - pos.x;
if self.multiline { if self.multiline {
font.layout_multiline(&self.text, max_width) font.layout_multiline(&self.text, max_width)
} else { } else {
@ -65,9 +65,9 @@ impl Label {
// TODO: this should return a LabelLayout which has a paint method. // TODO: this should return a LabelLayout which has a paint method.
// We can then split Widget::Ui in two: layout + allocating space, and painting. // We can then split Widget::Ui in two: layout + allocating space, and painting.
// this allows us to assemble lables, THEN detect interaction, THEN chose color style based on that. // this allows us to assemble lables, THEN detect interaction, THEN chose color style based on that.
// pub fn layout(self, region: &mut region) -> LabelLayout { } // pub fn layout(self, ui: &mut ui) -> LabelLayout { }
// TODO: a paint method for painting anywhere in a region. // TODO: a paint method for painting anywhere in a ui.
// This should be the easiest method of putting text anywhere. // This should be the easiest method of putting text anywhere.
} }
@ -79,11 +79,11 @@ macro_rules! label {
} }
impl Widget for Label { impl Widget for Label {
fn ui(self, region: &mut Region) -> GuiResponse { fn ui(self, ui: &mut Ui) -> GuiResponse {
let (text, text_size) = self.layout(region.cursor(), region); let (text, text_size) = self.layout(ui.cursor(), ui);
let interact = region.reserve_space(text_size, None); let interact = ui.reserve_space(text_size, None);
region.add_text(interact.rect.min, self.text_style, text, self.text_color); ui.add_text(interact.rect.min, self.text_style, text, self.text_color);
region.response(interact) ui.response(interact)
} }
} }
@ -117,20 +117,20 @@ impl Hyperlink {
} }
impl Widget for Hyperlink { impl Widget for Hyperlink {
fn ui(self, region: &mut Region) -> GuiResponse { fn ui(self, ui: &mut Ui) -> GuiResponse {
let color = color::LIGHT_BLUE; let color = color::LIGHT_BLUE;
let text_style = TextStyle::Body; let text_style = TextStyle::Body;
let id = region.make_child_id(&self.url); let id = ui.make_child_id(&self.url);
let font = &region.fonts()[text_style]; let font = &ui.fonts()[text_style];
let line_spacing = font.line_spacing(); let line_spacing = font.line_spacing();
// TODO: underline // TODO: underline
let (text, text_size) = font.layout_multiline(&self.text, region.available_width()); let (text, text_size) = font.layout_multiline(&self.text, ui.available_width());
let interact = region.reserve_space(text_size, Some(id)); let interact = ui.reserve_space(text_size, Some(id));
if interact.hovered { if interact.hovered {
region.ctx().output().cursor_icon = CursorIcon::PointingHand; ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
} }
if interact.clicked { if interact.clicked {
region.ctx().output().open_url = Some(self.url); ui.ctx().output().open_url = Some(self.url);
} }
if interact.hovered { if interact.hovered {
@ -138,20 +138,20 @@ impl Widget for Hyperlink {
for fragment in &text { for fragment in &text {
let pos = interact.rect.min; let pos = interact.rect.min;
let y = pos.y + fragment.y_offset + line_spacing; let y = pos.y + fragment.y_offset + line_spacing;
let y = region.round_to_pixel(y); let y = ui.round_to_pixel(y);
let min_x = pos.x + fragment.min_x(); let min_x = pos.x + fragment.min_x();
let max_x = pos.x + fragment.max_x(); let max_x = pos.x + fragment.max_x();
region.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::Line {
points: vec![pos2(min_x, y), pos2(max_x, y)], points: vec![pos2(min_x, y), pos2(max_x, y)],
color, color,
width: region.style().line_width, width: ui.style().line_width,
}); });
} }
} }
region.add_text(interact.rect.min, text_style, text, Some(color)); ui.add_text(interact.rect.min, text_style, text, Some(color));
region.response(interact) ui.response(interact)
} }
} }
@ -177,27 +177,27 @@ impl Button {
} }
impl Widget for Button { impl Widget for Button {
fn ui(self, region: &mut Region) -> GuiResponse { fn ui(self, ui: &mut Ui) -> GuiResponse {
let id = region.make_position_id(); let id = ui.make_position_id();
let text_style = TextStyle::Button; let text_style = TextStyle::Button;
let font = &region.fonts()[text_style]; let font = &ui.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, region.available_width()); let (text, text_size) = font.layout_multiline(&self.text, ui.available_width());
let padding = region.style().button_padding; let padding = ui.style().button_padding;
let mut size = text_size + 2.0 * padding; let mut size = text_size + 2.0 * padding;
size.y = size.y.max(region.style().clickable_diameter); size.y = size.y.max(ui.style().clickable_diameter);
let interact = region.reserve_space(size, Some(id)); let interact = ui.reserve_space(size, Some(id));
let mut text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * text_size.y); let mut text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * text_size.y);
text_cursor.y += 2.0; // TODO: why is this needed? text_cursor.y += 2.0; // TODO: why is this needed?
region.add_paint_cmd(PaintCmd::Rect { ui.add_paint_cmd(PaintCmd::Rect {
corner_radius: region.style().interact_corner_radius(&interact), corner_radius: ui.style().interact_corner_radius(&interact),
fill_color: region.style().interact_fill_color(&interact), fill_color: ui.style().interact_fill_color(&interact),
outline: region.style().interact_outline(&interact), outline: ui.style().interact_outline(&interact),
rect: interact.rect, rect: interact.rect,
}); });
let stroke_color = region.style().interact_stroke_color(&interact); let stroke_color = ui.style().interact_stroke_color(&interact);
let text_color = self.text_color.unwrap_or(stroke_color); let text_color = self.text_color.unwrap_or(stroke_color);
region.add_text(text_cursor, text_style, text, Some(text_color)); ui.add_text(text_cursor, text_style, text, Some(text_color));
region.response(interact) ui.response(interact)
} }
} }
@ -226,49 +226,48 @@ impl<'a> Checkbox<'a> {
} }
impl<'a> Widget for Checkbox<'a> { impl<'a> Widget for Checkbox<'a> {
fn ui(self, region: &mut Region) -> GuiResponse { fn ui(self, ui: &mut Ui) -> GuiResponse {
let id = region.make_position_id(); let id = ui.make_position_id();
let text_style = TextStyle::Button; let text_style = TextStyle::Button;
let font = &region.fonts()[text_style]; let font = &ui.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, region.available_width()); let (text, text_size) = font.layout_multiline(&self.text, ui.available_width());
let interact = region.reserve_space( let interact = ui.reserve_space(
region.style().button_padding ui.style().button_padding
+ vec2(region.style().start_icon_width, 0.0) + vec2(ui.style().start_icon_width, 0.0)
+ text_size + text_size
+ region.style().button_padding, + ui.style().button_padding,
Some(id), Some(id),
); );
let text_cursor = interact.rect.min let text_cursor =
+ region.style().button_padding interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
+ vec2(region.style().start_icon_width, 0.0);
if interact.clicked { if interact.clicked {
*self.checked = !*self.checked; *self.checked = !*self.checked;
} }
let (small_icon_rect, big_icon_rect) = region.style().icon_rectangles(interact.rect); let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
region.add_paint_cmd(PaintCmd::Rect { ui.add_paint_cmd(PaintCmd::Rect {
corner_radius: 3.0, corner_radius: 3.0,
fill_color: region.style().interact_fill_color(&interact), fill_color: ui.style().interact_fill_color(&interact),
outline: None, outline: None,
rect: big_icon_rect, rect: big_icon_rect,
}); });
let stroke_color = region.style().interact_stroke_color(&interact); let stroke_color = ui.style().interact_stroke_color(&interact);
if *self.checked { if *self.checked {
region.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::Line {
points: vec![ points: vec![
pos2(small_icon_rect.left(), small_icon_rect.center().y), pos2(small_icon_rect.left(), small_icon_rect.center().y),
pos2(small_icon_rect.center().x, small_icon_rect.bottom()), pos2(small_icon_rect.center().x, small_icon_rect.bottom()),
pos2(small_icon_rect.right(), small_icon_rect.top()), pos2(small_icon_rect.right(), small_icon_rect.top()),
], ],
color: stroke_color, color: stroke_color,
width: region.style().line_width, width: ui.style().line_width,
}); });
} }
let text_color = self.text_color.unwrap_or(stroke_color); let text_color = self.text_color.unwrap_or(stroke_color);
region.add_text(text_cursor, text_style, text, Some(text_color)); ui.add_text(text_cursor, text_style, text, Some(text_color));
region.response(interact) ui.response(interact)
} }
} }
@ -301,28 +300,27 @@ pub fn radio(checked: bool, text: impl Into<String>) -> RadioButton {
} }
impl Widget for RadioButton { impl Widget for RadioButton {
fn ui(self, region: &mut Region) -> GuiResponse { fn ui(self, ui: &mut Ui) -> GuiResponse {
let id = region.make_position_id(); let id = ui.make_position_id();
let text_style = TextStyle::Button; let text_style = TextStyle::Button;
let font = &region.fonts()[text_style]; let font = &ui.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, region.available_width()); let (text, text_size) = font.layout_multiline(&self.text, ui.available_width());
let interact = region.reserve_space( let interact = ui.reserve_space(
region.style().button_padding ui.style().button_padding
+ vec2(region.style().start_icon_width, 0.0) + vec2(ui.style().start_icon_width, 0.0)
+ text_size + text_size
+ region.style().button_padding, + ui.style().button_padding,
Some(id), Some(id),
); );
let text_cursor = interact.rect.min let text_cursor =
+ region.style().button_padding interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
+ vec2(region.style().start_icon_width, 0.0);
let fill_color = region.style().interact_fill_color(&interact); let fill_color = ui.style().interact_fill_color(&interact);
let stroke_color = region.style().interact_stroke_color(&interact); let stroke_color = ui.style().interact_stroke_color(&interact);
let (small_icon_rect, big_icon_rect) = region.style().icon_rectangles(interact.rect); let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
region.add_paint_cmd(PaintCmd::Circle { ui.add_paint_cmd(PaintCmd::Circle {
center: big_icon_rect.center(), center: big_icon_rect.center(),
fill_color, fill_color,
outline: None, outline: None,
@ -330,7 +328,7 @@ impl Widget for RadioButton {
}); });
if self.checked { if self.checked {
region.add_paint_cmd(PaintCmd::Circle { ui.add_paint_cmd(PaintCmd::Circle {
center: small_icon_rect.center(), center: small_icon_rect.center(),
fill_color: Some(stroke_color), fill_color: Some(stroke_color),
outline: None, outline: None,
@ -339,8 +337,8 @@ impl Widget for RadioButton {
} }
let text_color = self.text_color.unwrap_or(stroke_color); let text_color = self.text_color.unwrap_or(stroke_color);
region.add_text(text_cursor, text_style, text, Some(text_color)); ui.add_text(text_cursor, text_style, text, Some(text_color));
region.response(interact) ui.response(interact)
} }
} }
@ -386,13 +384,12 @@ impl Separator {
} }
impl Widget for Separator { impl Widget for Separator {
fn ui(self, region: &mut Region) -> GuiResponse { fn ui(self, ui: &mut Ui) -> GuiResponse {
let available_space = region.available_space(); let available_space = ui.available_space();
let extra = self.extra; let extra = self.extra;
let (points, interact) = match region.direction() { let (points, interact) = match ui.direction() {
Direction::Horizontal => { Direction::Horizontal => {
let interact = let interact = ui.reserve_space(vec2(self.min_spacing, available_space.y), None);
region.reserve_space(vec2(self.min_spacing, available_space.y), None);
( (
vec![ vec![
pos2(interact.rect.center().x, interact.rect.top() - extra), pos2(interact.rect.center().x, interact.rect.top() - extra),
@ -402,8 +399,7 @@ impl Widget for Separator {
) )
} }
Direction::Vertical => { Direction::Vertical => {
let interact = let interact = ui.reserve_space(vec2(available_space.x, self.min_spacing), None);
region.reserve_space(vec2(available_space.x, self.min_spacing), None);
( (
vec![ vec![
pos2(interact.rect.left() - extra, interact.rect.center().y), pos2(interact.rect.left() - extra, interact.rect.center().y),
@ -413,11 +409,11 @@ impl Widget for Separator {
) )
} }
}; };
region.add_paint_cmd(PaintCmd::Line { ui.add_paint_cmd(PaintCmd::Line {
points, points,
color: self.color, color: self.color,
width: self.line_width, width: self.line_width,
}); });
region.response(interact) ui.response(interact)
} }
} }

View file

@ -99,9 +99,9 @@ impl<'a> Slider<'a> {
} }
impl<'a> Widget for Slider<'a> { impl<'a> Widget for Slider<'a> {
fn ui(mut self, region: &mut Region) -> GuiResponse { fn ui(mut self, ui: &mut Ui) -> GuiResponse {
let text_style = TextStyle::Button; let text_style = TextStyle::Button;
let font = &region.fonts()[text_style]; let font = &ui.fonts()[text_style];
if let Some(text) = &self.text { if let Some(text) = &self.text {
if self.id.is_none() { if self.id.is_none() {
@ -116,35 +116,35 @@ impl<'a> Widget for Slider<'a> {
let slider_sans_text = Slider { text: None, ..self }; let slider_sans_text = Slider { text: None, ..self };
if text_on_top { if text_on_top {
// let (text, text_size) = font.layout_multiline(&full_text, region.available_width()); // let (text, text_size) = font.layout_multiline(&full_text, ui.available_width());
let (text, text_size) = font.layout_single_line(&full_text); let (text, text_size) = font.layout_single_line(&full_text);
let pos = region.reserve_space(text_size, None).rect.min; let pos = ui.reserve_space(text_size, None).rect.min;
region.add_text(pos, text_style, text, text_color); ui.add_text(pos, text_style, text, text_color);
slider_sans_text.ui(region) slider_sans_text.ui(ui)
} else { } else {
region.columns(2, |columns| { ui.columns(2, |columns| {
// Slider on the left: // Slider on the left:
let slider_response = columns[0].add(slider_sans_text); let slider_response = columns[0].add(slider_sans_text);
// Place the text in line with the slider on the left: // Place the text in line with the slider on the left:
columns[1].set_desired_height(slider_response.rect.height()); columns[1].set_desired_height(slider_response.rect.height());
columns[1].horizontal(|region| { columns[1].horizontal(|ui| {
region.set_align(Align::Center); ui.set_align(Align::Center);
region.add(Label::new(full_text).multiline(false)); ui.add(Label::new(full_text).multiline(false));
}); });
slider_response slider_response
}) })
} }
} else { } else {
let height = font.line_spacing().max(region.style().clickable_diameter); let height = font.line_spacing().max(ui.style().clickable_diameter);
let handle_radius = height / 2.5; let handle_radius = height / 2.5;
let id = self.id.unwrap_or_else(|| region.make_position_id()); let id = self.id.unwrap_or_else(|| ui.make_position_id());
let interact = region.reserve_space( let interact = ui.reserve_space(
Vec2 { Vec2 {
x: region.available_width(), x: ui.available_width(),
y: height, y: height,
}, },
Some(id), Some(id),
@ -156,7 +156,7 @@ impl<'a> Widget for Slider<'a> {
let range = self.range.clone(); let range = self.range.clone();
debug_assert!(range.start() <= range.end()); debug_assert!(range.start() <= range.end());
if let Some(mouse_pos) = region.input().mouse_pos { if let Some(mouse_pos) = ui.input().mouse_pos {
if interact.active { if interact.active {
self.set_value_f32(remap_clamp(mouse_pos.x, left..=right, range.clone())); self.set_value_f32(remap_clamp(mouse_pos.x, left..=right, range.clone()));
} }
@ -167,32 +167,32 @@ impl<'a> Widget for Slider<'a> {
let value = self.get_value_f32(); let value = self.get_value_f32();
let rect = interact.rect; let rect = interact.rect;
let rail_radius = region.round_to_pixel((height / 8.0).max(2.0)); let rail_radius = ui.round_to_pixel((height / 8.0).max(2.0));
let rail_rect = Rect::from_min_max( let rail_rect = Rect::from_min_max(
pos2(interact.rect.left(), rect.center().y - rail_radius), pos2(interact.rect.left(), rect.center().y - rail_radius),
pos2(interact.rect.right(), rect.center().y + rail_radius), pos2(interact.rect.right(), rect.center().y + rail_radius),
); );
let marker_center_x = remap_clamp(value, range, left..=right); let marker_center_x = remap_clamp(value, range, left..=right);
region.add_paint_cmd(PaintCmd::Rect { ui.add_paint_cmd(PaintCmd::Rect {
rect: rail_rect, rect: rail_rect,
corner_radius: rail_radius, corner_radius: rail_radius,
fill_color: Some(region.style().background_fill_color()), fill_color: Some(ui.style().background_fill_color()),
outline: Some(Outline::new(1.0, color::gray(200, 255))), // TODO outline: Some(Outline::new(1.0, color::gray(200, 255))), // TODO
}); });
region.add_paint_cmd(PaintCmd::Circle { ui.add_paint_cmd(PaintCmd::Circle {
center: pos2(marker_center_x, rail_rect.center().y), center: pos2(marker_center_x, rail_rect.center().y),
radius: handle_radius, radius: handle_radius,
fill_color: region.style().interact_fill_color(&interact), fill_color: ui.style().interact_fill_color(&interact),
outline: Some(Outline::new( outline: Some(Outline::new(
region.style().interact_stroke_width(&interact), ui.style().interact_stroke_width(&interact),
region.style().interact_stroke_color(&interact), ui.style().interact_stroke_color(&interact),
)), )),
}); });
} }
region.response(interact) ui.response(interact)
} }
} }
} }

View file

@ -4,7 +4,7 @@ use crate::*;
pub struct TextEdit<'t> { pub struct TextEdit<'t> {
text: &'t mut String, text: &'t mut String,
id: Option<Id>, id: Option<Id>,
text_style: TextStyle, // TODO: Option<TextStyle>, where None means "use the default for the region" text_style: TextStyle, // TODO: Option<TextStyle>, where None means "use the default for the current Ui"
text_color: Option<Color>, text_color: Option<Color>,
} }
@ -35,29 +35,29 @@ impl<'t> TextEdit<'t> {
} }
impl<'t> Widget for TextEdit<'t> { impl<'t> Widget for TextEdit<'t> {
fn ui(self, region: &mut Region) -> GuiResponse { fn ui(self, ui: &mut Ui) -> GuiResponse {
let id = region.make_child_id(self.id); let id = ui.make_child_id(self.id);
let font = &region.fonts()[self.text_style]; let font = &ui.fonts()[self.text_style];
let line_spacing = font.line_spacing(); let line_spacing = font.line_spacing();
let (text, text_size) = font.layout_multiline(self.text.as_str(), region.available_width()); let (text, text_size) = font.layout_multiline(self.text.as_str(), ui.available_width());
let desired_size = text_size.max(vec2(region.available_width(), line_spacing)); let desired_size = text_size.max(vec2(ui.available_width(), line_spacing));
let interact = region.reserve_space(desired_size, Some(id)); let interact = ui.reserve_space(desired_size, Some(id));
if interact.clicked { if interact.clicked {
region.request_kb_focus(id); ui.request_kb_focus(id);
} }
if interact.hovered { if interact.hovered {
region.output().cursor_icon = CursorIcon::Text; ui.output().cursor_icon = CursorIcon::Text;
} }
let has_kb_focus = region.has_kb_focus(id); let has_kb_focus = ui.has_kb_focus(id);
if has_kb_focus { if has_kb_focus {
for event in &region.input().events { for event in &ui.input().events {
match event { match event {
Event::Copy | Event::Cut => { Event::Copy | Event::Cut => {
// TODO: cut // TODO: cut
region.ctx().output().copied_text = self.text.clone(); ui.ctx().output().copied_text = self.text.clone();
} }
Event::Text(text) => { Event::Text(text) => {
if text == "\u{7f}" { if text == "\u{7f}" {
@ -76,35 +76,35 @@ impl<'t> Widget for TextEdit<'t> {
} }
} }
region.add_paint_cmd(PaintCmd::Rect { ui.add_paint_cmd(PaintCmd::Rect {
rect: interact.rect, rect: interact.rect,
corner_radius: 0.0, corner_radius: 0.0,
// fill_color: Some(color::BLACK), // fill_color: Some(color::BLACK),
fill_color: region.style().interact_fill_color(&interact), fill_color: ui.style().interact_fill_color(&interact),
// fill_color: Some(region.style().background_fill_color()), // fill_color: Some(ui.style().background_fill_color()),
outline: None, //Some(Outline::new(1.0, color::WHITE)), outline: None, //Some(Outline::new(1.0, color::WHITE)),
}); });
if has_kb_focus { if has_kb_focus {
let cursor_blink_hz = region.style().cursor_blink_hz; let cursor_blink_hz = ui.style().cursor_blink_hz;
let show_cursor = let show_cursor =
(region.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;
if show_cursor { if show_cursor {
let cursor_pos = if let Some(last) = text.last() { let cursor_pos = if let Some(last) = text.last() {
interact.rect.min + vec2(last.max_x(), last.y_offset) interact.rect.min + vec2(last.max_x(), last.y_offset)
} else { } else {
interact.rect.min interact.rect.min
}; };
region.add_paint_cmd(PaintCmd::line_segment( ui.add_paint_cmd(PaintCmd::line_segment(
(cursor_pos, cursor_pos + vec2(0.0, line_spacing)), (cursor_pos, cursor_pos + vec2(0.0, line_spacing)),
color::WHITE, color::WHITE,
region.style().text_cursor_width, ui.style().text_cursor_width,
)); ));
} }
} }
region.add_text(interact.rect.min, self.text_style, text, self.text_color); ui.add_text(interact.rect.min, self.text_style, text, self.text_color);
region.response(interact) ui.response(interact)
} }
} }

View file

@ -68,22 +68,22 @@ fn main() {
let emigui_start = Instant::now(); let emigui_start = Instant::now();
ctx.begin_frame(raw_input.clone()); // TODO: avoid clone ctx.begin_frame(raw_input.clone()); // TODO: avoid clone
let mut region = ctx.background_region(); let mut ui = ctx.fullscreen_ui();
let mut region = region.centered_column(region.available_width().min(480.0)); let mut ui = ui.centered_column(ui.available_width().min(480.0));
region.set_align(Align::Min); ui.set_align(Align::Min);
region.add(label!("Emigui running inside of Glium").text_style(emigui::TextStyle::Heading)); ui.add(label!("Emigui running inside of Glium").text_style(emigui::TextStyle::Heading));
if region.add(Button::new("Quit")).clicked { if ui.add(Button::new("Quit")).clicked {
running = false; running = false;
} }
region.add( ui.add(
label!( label!(
"CPU usage: {:.2} ms (excludes painting)", "CPU usage: {:.2} ms (excludes painting)",
1e3 * frame_times.average().unwrap_or_default() 1e3 * frame_times.average().unwrap_or_default()
) )
.text_style(TextStyle::Monospace), .text_style(TextStyle::Monospace),
); );
region.add( ui.add(
label!( label!(
"FPS: {:.1}", "FPS: {:.1}",
1.0 / frame_times.mean_time_interval().unwrap_or_default() 1.0 / frame_times.mean_time_interval().unwrap_or_default()
@ -98,15 +98,15 @@ fn main() {
.default_size(vec2(300.0, 600.0)) .default_size(vec2(300.0, 600.0))
// .mutate(|w| w.resize = w.resize.auto_expand_width(true)) // .mutate(|w| w.resize = w.resize.auto_expand_width(true))
// .resize(|r| r.auto_expand_width(true)) // .resize(|r| r.auto_expand_width(true))
.show(region.ctx(), |region| { .show(ui.ctx(), |ui| {
example_app.ui(region); example_app.ui(ui);
}); });
Window::new("Emigui settings") Window::new("Emigui settings")
.default_pos(pos2(450.0, 100.0)) .default_pos(pos2(450.0, 100.0))
.default_size(vec2(450.0, 500.0)) .default_size(vec2(450.0, 500.0))
.show(region.ctx(), |region| { .show(ui.ctx(), |ui| {
ctx.ui(region); ctx.ui(ui);
}); });
let (output, paint_batches) = ctx.end_frame(); let (output, paint_batches) = ctx.end_frame();

View file

@ -38,36 +38,36 @@ impl State {
let pixels_per_point = raw_input.pixels_per_point; let pixels_per_point = raw_input.pixels_per_point;
self.ctx.begin_frame(raw_input); self.ctx.begin_frame(raw_input);
let mut region = self.ctx.background_region(); let mut ui = self.ctx.fullscreen_ui();
let mut region = region.centered_column(region.available_width().min(480.0)); let mut ui = ui.centered_column(ui.available_width().min(480.0));
region.set_align(Align::Min); ui.set_align(Align::Min);
region.add(label!("Emigui!").text_style(TextStyle::Heading)); ui.add(label!("Emigui!").text_style(TextStyle::Heading));
region.add_label("Emigui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL."); ui.add_label("Emigui is an immediate mode GUI written in Rust, compiled to WebAssembly, rendered with WebGL.");
region.add_label( ui.add_label(
"Everything you see is rendered as textured triangles. There is no DOM. There are no HTML elements." "Everything you see is rendered as textured triangles. There is no DOM. There are no HTML elements."
); );
region.add_label("This is not JavaScript. This is Rust, running at 60 FPS. This is the web page, reinvented with game tech."); ui.add_label("This is not JavaScript. This is Rust, running at 60 FPS. This is the web page, reinvented with game tech.");
region.add_label("This is also work in progress, and not ready for production... yet :)"); ui.add_label("This is also work in progress, and not ready for production... yet :)");
region.horizontal(|region| { ui.horizontal(|ui| {
region.add_label("Project home page:"); ui.add_label("Project home page:");
region.add_hyperlink("https://github.com/emilk/emigui/"); ui.add_hyperlink("https://github.com/emilk/emigui/");
}); });
region.add(Separator::new()); ui.add(Separator::new());
region.set_align(Align::Min); ui.set_align(Align::Min);
region.add_label("WebGl painter info:"); ui.add_label("WebGl painter info:");
region.indent("webgl region", |region| { ui.indent("webgl region id", |ui| {
region.add_label(self.webgl_painter.debug_info()); ui.add_label(self.webgl_painter.debug_info());
}); });
region.add( ui.add(
label!( label!(
"CPU usage: {:.2} ms (excludes painting)", "CPU usage: {:.2} ms (excludes painting)",
1e3 * self.frame_times.average().unwrap_or_default() 1e3 * self.frame_times.average().unwrap_or_default()
) )
.text_style(TextStyle::Monospace), .text_style(TextStyle::Monospace),
); );
region.add( ui.add(
label!( label!(
"FPS: {:.1}", "FPS: {:.1}",
1.0 / self.frame_times.mean_time_interval().unwrap_or_default() 1.0 / self.frame_times.mean_time_interval().unwrap_or_default()
@ -80,15 +80,15 @@ impl State {
Window::new("Examples") Window::new("Examples")
.default_pos(pos2(32.0, 300.0)) .default_pos(pos2(32.0, 300.0))
.default_size(vec2(300.0, 400.0)) .default_size(vec2(300.0, 400.0))
.show(region.ctx(), |region| { .show(ui.ctx(), |ui| {
self.example_app.ui(region); self.example_app.ui(ui);
}); });
Window::new("Emigui settings") Window::new("Emigui settings")
.default_pos(pos2(400.0, 300.0)) .default_pos(pos2(400.0, 300.0))
.default_size(vec2(400.0, 400.0)) .default_size(vec2(400.0, 400.0))
.show(region.ctx(), |region| { .show(ui.ctx(), |ui| {
self.ctx.ui(region); self.ctx.ui(ui);
}); });
let bg_color = srgba(0, 0, 0, 0); // Use background css color. let bg_color = srgba(0, 0, 0, 0); // Use background css color.