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
* [ ] All of Context behind one mutex?
* [ } Break up Context into Input, State, Output ?
* [ ] Rename Region to something shorter?
* `region: &Region` `region.add(...)` :/
* `gui: &Gui` `gui.add(...)` :)
* `ui: &Ui` `ui.add(...)` :)
* [x] Rename Region to Ui
* [ ] Maybe find a shorter name for the library like `egui`?
### Global widget search

View file

@ -12,10 +12,10 @@ pub use {
// TODO
// 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 {
// fn begin(&mut self, parent: &mut Region) -> Region;
// fn end(self, parent: &mut Region, content: Region);
// fn begin(&mut self, parent: &mut Ui) -> Ui;
// 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
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>,
}
@ -44,9 +44,9 @@ 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!(
region.direction() == Direction::Vertical,
ui.direction() == Direction::Vertical,
"Horizontal collapsing is unimplemented"
);
let Self {
@ -57,63 +57,63 @@ impl CollapsingHeader {
// TODO: horizontal layout, with icon and text as labels. Inser background behind using Frame.
let title = &label.text; // TODO: not this
let id = region.make_unique_id(title);
let text_pos = region.cursor() + vec2(region.style().indent, 0.0);
let (title, text_size) = label.layout(text_pos, region);
let id = ui.make_unique_id(title);
let text_pos = ui.cursor() + vec2(ui.style().indent, 0.0);
let (title, text_size) = label.layout(text_pos, ui);
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(
desired_width,
text_size.y + 2.0 * region.style().button_padding.y,
text_size.y + 2.0 * ui.style().button_padding.y,
),
Some(id),
);
let text_pos = pos2(text_pos.x, interact.rect.center().y - text_size.y / 2.0);
let mut state = {
let mut memory = region.memory();
let mut memory = ui.memory();
let mut state = memory.collapsing_headers.entry(id).or_insert(State {
open: default_open,
..Default::default()
});
if interact.clicked {
state.open = !state.open;
state.toggle_time = region.input().time;
state.toggle_time = ui.input().time;
}
*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,
label.text_style,
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,
PaintCmd::Rect {
corner_radius: region.style().interact_corner_radius(&interact),
fill_color: region.style().interact_fill_color(&interact),
outline: region.style().interact_outline(&interact),
corner_radius: ui.style().interact_corner_radius(&interact),
fill_color: ui.style().interact_fill_color(&interact),
outline: ui.style().interact_outline(&interact),
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 time_since_toggle = (region.input().time - state.toggle_time) as f32;
let time_since_toggle = time_since_toggle + region.input().dt; // Instant feedback
let animation_time = ui.style().animation_time;
let time_since_toggle = (ui.input().time - state.toggle_time) as f32;
let time_since_toggle = time_since_toggle + ui.input().dt; // Instant feedback
let animate = time_since_toggle < animation_time;
if animate {
region.indent(id, |child_region| {
ui.indent(id, |child_ui| {
let max_height = if state.open {
let full_height = state.open_height.unwrap_or(1000.0);
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)
};
let mut clip_rect = child_region.clip_rect();
clip_rect.max.y = clip_rect.max.y.min(child_region.cursor().y + max_height);
child_region.set_clip_rect(clip_rect);
let mut clip_rect = child_ui.clip_rect();
clip_rect.max.y = clip_rect.max.y.min(child_ui.cursor().y + max_height);
child_ui.set_clip_rect(clip_rect);
let top_left = child_region.top_left();
add_contents(child_region);
let top_left = child_ui.top_left();
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:
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_region.force_set_child_bounds(child_bounds);
child_ui.force_set_child_bounds(child_bounds);
});
} 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);
}
region.memory().collapsing_headers.insert(id, state);
region.response(interact)
ui.memory().collapsing_headers.insert(id, state);
ui.response(interact)
}
}
fn paint_icon(region: &mut Region, state: &State, interact: &InteractInfo) {
let stroke_color = region.style().interact_stroke_color(interact);
let stroke_width = region.style().interact_stroke_width(interact);
fn paint_icon(ui: &mut Ui, state: &State, interact: &InteractInfo) {
let stroke_color = ui.style().interact_stroke_color(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(
interact.rect.left() + region.style().indent / 2.0,
interact.rect.left() + ui.style().indent / 2.0,
interact.rect.center().y,
));
// Draw a minus:
region.add_paint_cmd(PaintCmd::Line {
ui.add_paint_cmd(PaintCmd::Line {
points: vec![
pos2(small_icon_rect.left(), 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 {
// Draw it as a plus:
region.add_paint_cmd(PaintCmd::Line {
ui.add_paint_cmd(PaintCmd::Line {
points: vec![
pos2(small_icon_rect.center().x, small_icon_rect.top()),
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 has no frame or own size.
//! It is the foundation for a window
@ -49,7 +49,7 @@ 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 id = ctx.register_unique_id(self.id, "Floating", default_pos);
let layer = Layer::Window(id);
@ -67,14 +67,14 @@ impl Floating {
};
state.pos = state.pos.round();
let mut region = Region::new(
let mut ui = Ui::new(
ctx.clone(),
layer,
id,
Rect::from_min_size(state.pos, Vec2::infinity()),
);
add_contents(&mut region);
state.size = region.bounding_size().ceil();
add_contents(&mut ui);
state.size = ui.bounding_size().ceil();
let rect = Rect::from_min_size(state.pos, state.size);
let clip_rect = Rect::everything();

View file

@ -15,26 +15,26 @@ impl Frame {
}
impl Frame {
pub fn show(self, region: &mut Region, add_contents: impl FnOnce(&mut Region)) {
let style = region.style();
pub fn show(self, ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
let style = ui.style();
let margin = self.margin.unwrap_or_default();
let outer_pos = region.cursor();
let outer_pos = ui.cursor();
let inner_rect =
Rect::from_min_size(outer_pos + margin, region.available_space() - 2.0 * margin);
let where_to_put_background = region.paint_list_len();
Rect::from_min_size(outer_pos + margin, ui.available_space() - 2.0 * margin);
let where_to_put_background = ui.paint_list_len();
let mut child_region = region.child_region(inner_rect);
add_contents(&mut child_region);
let mut child_ui = ui.child_ui(inner_rect);
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 outer_rect = Rect::from_min_size(outer_pos, margin + inner_size + margin);
let corner_radius = style.window.corner_radius;
let fill_color = style.background_fill_color();
region.insert_paint_cmd(
ui.insert_paint_cmd(
where_to_put_background,
PaintCmd::Rect {
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?
}
}

View file

@ -13,7 +13,7 @@ pub struct Resize {
/// If false, we are no enabled
resizable: bool,
// Will still try to stay within parent region bounds
// Will still try to stay within parent ui bounds
min_size: Vec2,
max_size: Vec2,
@ -132,17 +132,17 @@ impl Resize {
// TODO: a common trait for Things that follow this pattern
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 {
return add_contents(region);
return add_contents(ui);
}
let id = region.make_child_id("scroll");
self.min_size = self.min_size.min(region.available_space());
self.max_size = self.max_size.min(region.available_space());
let id = ui.make_child_id("scroll");
self.min_size = self.min_size.min(ui.available_space());
self.max_size = self.max_size.min(ui.available_space());
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),
None => {
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);
let last_frame_size = state.size;
let position = region.cursor();
let position = ui.cursor();
// Resize-corner:
let corner_size = Vec2::splat(16.0); // TODO: style
@ -161,10 +161,10 @@ impl Resize {
position + state.size + self.handle_offset - 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 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.
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 mut content_clip_rect = region
let mut content_clip_rect = ui
.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.
// After laying out the contents, we might be much bigger.
@ -192,12 +192,12 @@ impl Resize {
content_clip_rect.max = content_clip_rect
.max
.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);
contents_region.set_clip_rect(content_clip_rect);
add_contents(&mut contents_region);
contents_region.bounding_size()
let mut contents_ui = ui.child_ui(inner_rect);
contents_ui.set_clip_rect(content_clip_rect);
add_contents(&mut contents_ui);
contents_ui.bounding_size()
};
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.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 {
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) {
let color = region.style().interact_stroke_color(interact);
let width = region.style().interact_stroke_width(interact);
fn paint_resize_corner(ui: &mut Ui, interact: &InteractInfo) {
let color = ui.style().interact_stroke_color(interact);
let width = ui.style().interact_stroke_width(interact);
let corner = interact.rect.right_bottom().round(); // TODO: round to pixels
let mut w = 2.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)),
color,
width,

View file

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

View file

@ -85,7 +85,7 @@ 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 {
title_label,
floating,
@ -96,12 +96,12 @@ impl Window {
frame.margin = Some(frame.margin.unwrap_or_else(|| ctx.style().window_padding));
// TODO: easier way to compose these
floating.show(ctx, |region| {
frame.show(region, |region| {
resize.show(region, |region| {
region.add(title_label);
region.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents
scroll.show(region, |region| add_contents(region))
floating.show(ctx, |ui| {
frame.show(ui, |ui| {
resize.show(ui, |ui| {
ui.add(title_label);
ui.add(Separator::new().line_width(1.0)); // TODO: nicer way to split window title from contents
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.
/// Regions keep an Arc pointer to this.
/// This allows us to create several child regions at once,
/// `Ui`:s keep an Arc pointer to this.
/// This allows us to create several child `Ui`:s at once,
/// all working against the same shared Context.
pub struct Context {
/// The default style for new regions
/// The default style for new `Ui`:s
style: Mutex<Style>,
mesher_options: Mutex<mesher::MesherOptions>,
fonts: Arc<Fonts>,
@ -207,10 +207,10 @@ impl Context {
// ---------------------------------------------------------------------
/// A region for the entire screen, behind any windows.
pub fn background_region(self: &Arc<Self>) -> Region {
/// An ui for the entire screen, behind any windows.
pub fn fullscreen_ui(self: &Arc<Self>) -> Ui {
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 {
pub fn ui(&self, region: &mut Region) {
pub fn ui(&self, ui: &mut Ui) {
use crate::containers::*;
region.collapsing("Style", |region| {
self.mesher_options.lock().ui(region);
self.style_ui(region);
ui.collapsing("Style", |ui| {
self.mesher_options.lock().ui(ui);
self.style_ui(ui);
});
region.collapsing("Fonts", |region| {
ui.collapsing("Fonts", |ui| {
let old_font_definitions = self.fonts().definitions();
let mut new_font_definitions = old_font_definitions.clone();
font_definitions_ui(&mut new_font_definitions, region);
self.fonts().texture().ui(region);
font_definitions_ui(&mut new_font_definitions, ui);
self.fonts().texture().ui(ui);
if *old_font_definitions != new_font_definitions {
let fonts =
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")
.default_open()
.show(region, |region| {
region.ctx().last_raw_input().clone().ui(region)
});
.show(ui, |ui| ui.ctx().last_raw_input().clone().ui(ui));
CollapsingHeader::new("Input")
.default_open()
.show(region, |region| region.input().clone().ui(region));
.show(ui, |ui| ui.input().clone().ui(ui));
});
region.collapsing("Stats", |region| {
region.add(label!(
ui.collapsing("Stats", |ui| {
ui.add(label!(
"Screen size: {} x {} points, pixels_per_point: {}",
region.input().screen_size.x,
region.input().screen_size.y,
region.input().pixels_per_point,
ui.input().screen_size.x,
ui.input().screen_size.y,
ui.input().pixels_per_point,
));
if let Some(mouse_pos) = region.input().mouse_pos {
region.add(label!("mouse_pos: {:.2} x {:.2}", mouse_pos.x, mouse_pos.y,));
if let Some(mouse_pos) = ui.input().mouse_pos {
ui.add(label!("mouse_pos: {:.2} x {:.2}", mouse_pos.x, mouse_pos.y,));
} else {
region.add_label("mouse_pos: None");
ui.add_label("mouse_pos: None");
}
region.add(label!("Painting:").text_style(TextStyle::Heading));
self.paint_stats.lock().ui(region);
ui.add(label!("Painting:").text_style(TextStyle::Heading));
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::*;
for (text_style, (_family, size)) in font_definitions.iter_mut() {
// TODO: radiobutton for family
region.add(
ui.add(
Slider::f32(size, 4.0..=40.0)
.precision(0)
.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();
}
}
impl Context {
pub fn style_ui(&self, region: &mut Region) {
pub fn style_ui(&self, ui: &mut Ui) {
let mut style = self.style();
style.ui(region);
style.ui(ui);
self.set_style(style);
}
}
impl mesher::MesherOptions {
pub fn ui(&mut self, region: &mut Region) {
pub fn ui(&mut self, ui: &mut Ui) {
use crate::widgets::*;
region.add(Checkbox::new(&mut self.anti_alias, "Antialias"));
region.add(Checkbox::new(
ui.add(Checkbox::new(&mut self.anti_alias, "Antialias"));
ui.add(Checkbox::new(
&mut self.debug_paint_clip_rects,
"Paint Clip Rects (debug)",
));
@ -513,14 +511,12 @@ impl mesher::MesherOptions {
}
impl PaintStats {
pub fn ui(&self, region: &mut Region) {
region
.add(label!("Batches: {}", self.num_batches))
pub fn ui(&self, ui: &mut Ui) {
ui.add(label!("Batches: {}", self.num_batches))
.tooltip_text("Number of separate clip rectanlges");
region
.add(label!("Primitives: {}", self.num_primitives))
ui.add(label!("Primitives: {}", self.num_primitives))
.tooltip_text("Boxes, circles, text areas etc");
region.add(label!("Vertices: {}", self.num_vertices));
region.add(label!("Triangles: {}", self.num_triangles));
ui.add(label!("Vertices: {}", self.num_vertices));
ui.add(label!("Triangles: {}", self.num_triangles));
}
}

View file

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

View file

@ -153,41 +153,41 @@ impl GuiInput {
}
impl RawInput {
pub fn ui(&self, region: &mut crate::Region) {
pub fn ui(&self, ui: &mut crate::Ui) {
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!
region.add(label!("mouse_down: {}", self.mouse_down));
region.add(label!("mouse_pos: {:.1?}", self.mouse_pos));
region.add(label!("scroll_delta: {:?}", self.scroll_delta));
region.add(label!("screen_size: {:?}", self.screen_size));
region.add(label!("pixels_per_point: {}", self.pixels_per_point));
region.add(label!("time: {:.3} s", self.time));
region.add(label!("events: {:?}", self.events));
region.add(label!("dropped_files: {:?}", self.dropped_files));
region.add(label!("hovered_files: {:?}", self.hovered_files));
ui.add(label!("mouse_down: {}", self.mouse_down));
ui.add(label!("mouse_pos: {:.1?}", self.mouse_pos));
ui.add(label!("scroll_delta: {:?}", self.scroll_delta));
ui.add(label!("screen_size: {:?}", self.screen_size));
ui.add(label!("pixels_per_point: {}", self.pixels_per_point));
ui.add(label!("time: {:.3} s", self.time));
ui.add(label!("events: {:?}", self.events));
ui.add(label!("dropped_files: {:?}", self.dropped_files));
ui.add(label!("hovered_files: {:?}", self.hovered_files));
}
}
impl GuiInput {
pub fn ui(&self, region: &mut crate::Region) {
pub fn ui(&self, ui: &mut crate::Ui) {
use crate::label;
region.add(label!("mouse_down: {}", self.mouse_down));
region.add(label!("mouse_pressed: {}", self.mouse_pressed));
region.add(label!("mouse_released: {}", self.mouse_released));
region.add(label!("mouse_pos: {:?}", self.mouse_pos));
region.add(label!("mouse_move: {:?}", self.mouse_move));
region.add(label!(
ui.add(label!("mouse_down: {}", self.mouse_down));
ui.add(label!("mouse_pressed: {}", self.mouse_pressed));
ui.add(label!("mouse_released: {}", self.mouse_released));
ui.add(label!("mouse_pos: {:?}", self.mouse_pos));
ui.add(label!("mouse_move: {:?}", self.mouse_move));
ui.add(label!(
"mouse_velocity: [{:3.0} {:3.0}] points/sec",
self.mouse_velocity.x,
self.mouse_velocity.y
));
region.add(label!("scroll_delta: {:?}", self.scroll_delta));
region.add(label!("screen_size: {:?}", self.screen_size));
region.add(label!("pixels_per_point: {}", self.pixels_per_point));
region.add(label!("time: {:.3} s", self.time));
region.add(label!("events: {:?}", self.events));
region.add(label!("dropped_files: {:?}", self.dropped_files));
region.add(label!("hovered_files: {:?}", self.hovered_files));
ui.add(label!("scroll_delta: {:?}", self.scroll_delta));
ui.add(label!("screen_size: {:?}", self.screen_size));
ui.add(label!("pixels_per_point: {}", self.pixels_per_point));
ui.add(label!("time: {:.3} s", self.time));
ui.add(label!("events: {:?}", self.events));
ui.add(label!("dropped_files: {:?}", self.dropped_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)
pub active: bool,
/// The region of the screen we are talking about
/// The area of the screen we are talking about
pub rect: Rect,
/// Used for optionally showing a tooltip
@ -24,7 +24,7 @@ pub struct GuiResponse {
impl GuiResponse {
/// 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 let Some(mouse_pos) = self.ctx.input().mouse_pos {
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)
/// 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 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 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:
let inner_size = contents_region.bounding_size();
let inner_size = contents_ui.bounding_size();
let outer_size = inner_size + 2.0 * window_padding;
let rect = Rect::from_min_size(window_pos, outer_size);

View file

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

View file

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

View file

@ -91,19 +91,19 @@ impl TextureAtlas {
}
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};
region.add(label!(
ui.add(label!(
"Texture size: {} x {} (hover to zoom)",
self.width,
self.height
));
let mut size = vec2(self.width as f32, self.height as f32);
if size.x > region.available_width() {
size *= region.available_width() / size.x;
if size.x > ui.available_width() {
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 top_left = Vertex {
pos: rect.min,
@ -117,12 +117,12 @@ impl Texture {
};
let mut mesh = Mesh::default();
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 {
show_popup(region.ctx(), mouse_pos, |region| {
let zoom_rect = region.reserve_space(vec2(128.0, 128.0), None).rect;
show_popup(ui.ctx(), mouse_pos, |ui| {
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)
.round();
let v =
@ -145,7 +145,7 @@ impl Texture {
};
let mut mesh = Mesh::default();
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
/// with a type of layout (horizontal or vertical).
/// TODO: make Region a trait so we can have type-safe `HorizontalRegion` etc?
pub struct Region {
pub struct Ui {
/// How we access input, output and memory
ctx: Arc<Context>,
/// ID of this region.
/// Generated based on id of parent region together with
/// ID of this ui.
/// Generated based on id of parent ui together with
/// 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.
id: Id,
/// Where to put the graphics output of this Region
/// Where to put the graphics output of this Ui
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.
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).
/// Note that the size may be infinite in one or both dimensions.
/// The widgets will TRY to fit within the rect,
/// 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 ?
/// 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.
/// You can think of this as the minimum size.
child_bounds: Rect, // TODO: rename as min_rect ?
/// Overide default style in this region
/// Overide default style in this ui
style: Style,
// Layout stuff follows. TODO: move to own type and abstract.
@ -54,13 +53,13 @@ pub struct Region {
cursor: Pos2,
}
impl Region {
impl Ui {
// ------------------------------------------------------------------------
// Creation:
pub fn new(ctx: Arc<Context>, layer: Layer, id: Id, rect: Rect) -> Self {
let style = ctx.style();
Region {
Ui {
ctx,
id,
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
// .clip_rect
// .intersect(&child_rect.expand(self.style().clip_rect_margin));
let clip_rect = self.clip_rect(); // Keep it unless the child explciitly desires differently
Region {
Ui {
ctx: self.ctx.clone(),
layer: self.layer,
style: self.style,
@ -111,7 +110,7 @@ impl Region {
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 {
&self.style
}
@ -136,7 +135,7 @@ impl Region {
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
/// than its contents.
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).
pub fn top_left(&self) -> Pos2 {
// If a child doesn't fit in desired_rect, we have effectively expanded:
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.
pub fn bottom_right(&self) -> Pos2 {
// If a child doesn't fit in desired_rect, we have effectively expanded:
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 of the containted children.
pub fn rect(&self) -> Rect {
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.
pub fn set_desired_width(&mut self, width: f32) {
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;
}
/// 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.
pub fn set_desired_height(&mut self, height: f32) {
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()
}
/// 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) {
self.child_bounds.extend_with(rect.min);
self.child_bounds.extend_with(rect.max);
@ -257,7 +256,7 @@ impl Region {
/// Will warn if the returned id is not guaranteed unique.
/// 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.
pub fn make_unique_id<IdSource>(&self, id_source: &IdSource) -> Id
where
@ -282,7 +281,7 @@ impl Region {
// ------------------------------------------------------------------------
// Interaction
/// Check for clicks on this entire region (rect())
/// Check for clicks on this entire ui (rect())
pub fn interact_whole(&self) -> InteractInfo {
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.
/// Returns where to put the widget.
@ -324,7 +323,7 @@ impl Region {
let child_pos = self.reserve_space_impl(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 {
rect,
corner_radius: 0.0,
@ -446,7 +445,7 @@ impl Region {
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)`.
/// If you want to draw text floating on top of everything,
/// consider using `Context.floating_text` instead.
@ -503,38 +502,38 @@ impl Region {
}
// ------------------------------------------------------------------------
// Addding Containers / Sub-Regions:
// Addding Containers / Sub-uis:
pub fn collapsing(
&mut self,
text: impl Into<String>,
add_contents: impl FnOnce(&mut Region),
add_contents: impl FnOnce(&mut Ui),
) -> GuiResponse {
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.
/// Actual size may be much smaller if `avilable_size()` is not enough.
/// 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!
/// After `add_contents` is called the contents of `bounding_size`
/// will decide how much space will be used in the parent region.
pub fn add_custom_contents(&mut self, size: Vec2, add_contents: impl FnOnce(&mut 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 Ui)) {
let size = size.min(self.available_space());
let child_rect = Rect::from_min_size(self.cursor, size);
let mut child_region = Region {
..self.child_region(child_rect)
let mut child_ui = Ui {
..self.child_ui(child_rect)
};
add_contents(&mut child_region);
self.reserve_space(child_region.bounding_size(), None);
add_contents(&mut child_ui);
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(
&mut self,
id_source: impl Hash,
add_contents: impl FnOnce(&mut Region),
add_contents: impl FnOnce(&mut Ui),
) -> InteractInfo {
assert!(
self.dir == Direction::Vertical,
@ -542,15 +541,15 @@ impl Region {
);
let indent = vec2(self.style.indent, 0.0);
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),
align: Align::Min,
..self.child_region(child_rect)
..self.child_ui(child_rect)
};
add_contents(&mut child_region);
let size = child_region.bounding_size();
add_contents(&mut child_ui);
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 = line_start.round(); // TODO: round to pixel instead
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)
}
pub fn left_column(&mut self, width: f32) -> Region {
pub fn left_column(&mut self, width: f32) -> Ui {
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)
}
pub fn right_column(&mut self, width: f32) -> Region {
pub fn right_column(&mut self, width: f32) -> Ui {
self.column(Align::Max, width)
}
/// A column region with a given width.
pub fn column(&mut self, column_position: Align, width: f32) -> Region {
/// A column ui with a given width.
pub fn column(&mut self, column_position: Align, width: f32) -> Ui {
let x = match column_position {
Align::Min => 0.0,
Align::Center => self.available_width() / 2.0 - width / 2.0,
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),
vec2(width, self.available_height()),
))
}
/// Start a region with horizontal layout
pub fn horizontal(&mut self, add_contents: impl FnOnce(&mut Region)) {
/// Start a ui with horizontal layout
pub fn horizontal(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self.inner_layout(Direction::Horizontal, Align::Min, add_contents)
}
/// Start a region with vertical layout
pub fn vertical(&mut self, add_contents: impl FnOnce(&mut Region)) {
/// Start a ui with vertical layout
pub fn vertical(&mut self, add_contents: impl FnOnce(&mut Ui)) {
self.inner_layout(Direction::Vertical, Align::Min, add_contents)
}
@ -605,20 +604,20 @@ impl Region {
add_contents: impl FnOnce(&mut Self),
) {
let child_rect = Rect::from_min_max(self.cursor, self.bottom_right());
let mut child_region = Self {
let mut child_ui = Self {
dir,
align,
..self.child_region(child_rect)
..self.child_ui(child_rect)
};
add_contents(&mut child_region);
let size = child_region.bounding_size();
add_contents(&mut child_ui);
let size = child_ui.bounding_size();
self.reserve_space(size, None);
}
/// Temporarily split split a vertical layout into several columns.
///
/// ``` ignore
/// region.columns(2, |columns| {
/// ui.columns(2, |columns| {
/// columns[0].add(emigui::widgets::label!("First column"));
/// columns[1].add(emigui::widgets::label!("Second column"));
/// });
@ -641,7 +640,7 @@ impl Region {
Self {
id: self.make_child_id(&("column", col_idx)),
dir: Direction::Vertical,
..self.child_region(child_rect)
..self.child_ui(child_rect)
}
})
.collect();
@ -654,8 +653,8 @@ impl Region {
}
let mut max_height = 0.0;
for region in columns {
let size = region.bounding_size();
for ui in columns {
let size = ui.bounding_size();
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 {
fn ui(self, region: &mut Region) -> GuiResponse;
fn ui(self, ui: &mut Ui) -> GuiResponse;
}
// ----------------------------------------------------------------------------
@ -23,7 +23,7 @@ pub struct Label {
// TODO: not pub
pub(crate) text: String,
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>,
}
@ -52,9 +52,9 @@ impl Label {
self
}
pub fn layout(&self, pos: Pos2, region: &Region) -> (Vec<font::TextFragment>, Vec2) {
let font = &region.fonts()[self.text_style];
let max_width = region.rect().right() - pos.x;
pub fn layout(&self, pos: Pos2, ui: &Ui) -> (Vec<font::TextFragment>, Vec2) {
let font = &ui.fonts()[self.text_style];
let max_width = ui.rect().right() - pos.x;
if self.multiline {
font.layout_multiline(&self.text, max_width)
} else {
@ -65,9 +65,9 @@ impl Label {
// TODO: this should return a LabelLayout which has a paint method.
// 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.
// 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.
}
@ -79,11 +79,11 @@ macro_rules! label {
}
impl Widget for Label {
fn ui(self, region: &mut Region) -> GuiResponse {
let (text, text_size) = self.layout(region.cursor(), region);
let interact = region.reserve_space(text_size, None);
region.add_text(interact.rect.min, self.text_style, text, self.text_color);
region.response(interact)
fn ui(self, ui: &mut Ui) -> GuiResponse {
let (text, text_size) = self.layout(ui.cursor(), ui);
let interact = ui.reserve_space(text_size, None);
ui.add_text(interact.rect.min, self.text_style, text, self.text_color);
ui.response(interact)
}
}
@ -117,20 +117,20 @@ impl 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 text_style = TextStyle::Body;
let id = region.make_child_id(&self.url);
let font = &region.fonts()[text_style];
let id = ui.make_child_id(&self.url);
let font = &ui.fonts()[text_style];
let line_spacing = font.line_spacing();
// TODO: underline
let (text, text_size) = font.layout_multiline(&self.text, region.available_width());
let interact = region.reserve_space(text_size, Some(id));
let (text, text_size) = font.layout_multiline(&self.text, ui.available_width());
let interact = ui.reserve_space(text_size, Some(id));
if interact.hovered {
region.ctx().output().cursor_icon = CursorIcon::PointingHand;
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
}
if interact.clicked {
region.ctx().output().open_url = Some(self.url);
ui.ctx().output().open_url = Some(self.url);
}
if interact.hovered {
@ -138,20 +138,20 @@ impl Widget for Hyperlink {
for fragment in &text {
let pos = interact.rect.min;
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 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)],
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 {
fn ui(self, region: &mut Region) -> GuiResponse {
let id = region.make_position_id();
fn ui(self, ui: &mut Ui) -> GuiResponse {
let id = ui.make_position_id();
let text_style = TextStyle::Button;
let font = &region.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, region.available_width());
let padding = region.style().button_padding;
let font = &ui.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, ui.available_width());
let padding = ui.style().button_padding;
let mut size = text_size + 2.0 * padding;
size.y = size.y.max(region.style().clickable_diameter);
let interact = region.reserve_space(size, Some(id));
size.y = size.y.max(ui.style().clickable_diameter);
let interact = ui.reserve_space(size, Some(id));
let mut text_cursor = interact.rect.left_center() + vec2(padding.x, -0.5 * text_size.y);
text_cursor.y += 2.0; // TODO: why is this needed?
region.add_paint_cmd(PaintCmd::Rect {
corner_radius: region.style().interact_corner_radius(&interact),
fill_color: region.style().interact_fill_color(&interact),
outline: region.style().interact_outline(&interact),
ui.add_paint_cmd(PaintCmd::Rect {
corner_radius: ui.style().interact_corner_radius(&interact),
fill_color: ui.style().interact_fill_color(&interact),
outline: ui.style().interact_outline(&interact),
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);
region.add_text(text_cursor, text_style, text, Some(text_color));
region.response(interact)
ui.add_text(text_cursor, text_style, text, Some(text_color));
ui.response(interact)
}
}
@ -226,49 +226,48 @@ impl<'a> Checkbox<'a> {
}
impl<'a> Widget for Checkbox<'a> {
fn ui(self, region: &mut Region) -> GuiResponse {
let id = region.make_position_id();
fn ui(self, ui: &mut Ui) -> GuiResponse {
let id = ui.make_position_id();
let text_style = TextStyle::Button;
let font = &region.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, region.available_width());
let interact = region.reserve_space(
region.style().button_padding
+ vec2(region.style().start_icon_width, 0.0)
let font = &ui.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, ui.available_width());
let interact = ui.reserve_space(
ui.style().button_padding
+ vec2(ui.style().start_icon_width, 0.0)
+ text_size
+ region.style().button_padding,
+ ui.style().button_padding,
Some(id),
);
let text_cursor = interact.rect.min
+ region.style().button_padding
+ vec2(region.style().start_icon_width, 0.0);
let text_cursor =
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
if interact.clicked {
*self.checked = !*self.checked;
}
let (small_icon_rect, big_icon_rect) = region.style().icon_rectangles(interact.rect);
region.add_paint_cmd(PaintCmd::Rect {
let (small_icon_rect, big_icon_rect) = ui.style().icon_rectangles(interact.rect);
ui.add_paint_cmd(PaintCmd::Rect {
corner_radius: 3.0,
fill_color: region.style().interact_fill_color(&interact),
fill_color: ui.style().interact_fill_color(&interact),
outline: None,
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 {
region.add_paint_cmd(PaintCmd::Line {
ui.add_paint_cmd(PaintCmd::Line {
points: vec![
pos2(small_icon_rect.left(), small_icon_rect.center().y),
pos2(small_icon_rect.center().x, small_icon_rect.bottom()),
pos2(small_icon_rect.right(), small_icon_rect.top()),
],
color: stroke_color,
width: region.style().line_width,
width: ui.style().line_width,
});
}
let text_color = self.text_color.unwrap_or(stroke_color);
region.add_text(text_cursor, text_style, text, Some(text_color));
region.response(interact)
ui.add_text(text_cursor, text_style, text, Some(text_color));
ui.response(interact)
}
}
@ -301,28 +300,27 @@ pub fn radio(checked: bool, text: impl Into<String>) -> RadioButton {
}
impl Widget for RadioButton {
fn ui(self, region: &mut Region) -> GuiResponse {
let id = region.make_position_id();
fn ui(self, ui: &mut Ui) -> GuiResponse {
let id = ui.make_position_id();
let text_style = TextStyle::Button;
let font = &region.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, region.available_width());
let interact = region.reserve_space(
region.style().button_padding
+ vec2(region.style().start_icon_width, 0.0)
let font = &ui.fonts()[text_style];
let (text, text_size) = font.layout_multiline(&self.text, ui.available_width());
let interact = ui.reserve_space(
ui.style().button_padding
+ vec2(ui.style().start_icon_width, 0.0)
+ text_size
+ region.style().button_padding,
+ ui.style().button_padding,
Some(id),
);
let text_cursor = interact.rect.min
+ region.style().button_padding
+ vec2(region.style().start_icon_width, 0.0);
let text_cursor =
interact.rect.min + ui.style().button_padding + vec2(ui.style().start_icon_width, 0.0);
let fill_color = region.style().interact_fill_color(&interact);
let stroke_color = region.style().interact_stroke_color(&interact);
let fill_color = ui.style().interact_fill_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(),
fill_color,
outline: None,
@ -330,7 +328,7 @@ impl Widget for RadioButton {
});
if self.checked {
region.add_paint_cmd(PaintCmd::Circle {
ui.add_paint_cmd(PaintCmd::Circle {
center: small_icon_rect.center(),
fill_color: Some(stroke_color),
outline: None,
@ -339,8 +337,8 @@ impl Widget for RadioButton {
}
let text_color = self.text_color.unwrap_or(stroke_color);
region.add_text(text_cursor, text_style, text, Some(text_color));
region.response(interact)
ui.add_text(text_cursor, text_style, text, Some(text_color));
ui.response(interact)
}
}
@ -386,13 +384,12 @@ impl Separator {
}
impl Widget for Separator {
fn ui(self, region: &mut Region) -> GuiResponse {
let available_space = region.available_space();
fn ui(self, ui: &mut Ui) -> GuiResponse {
let available_space = ui.available_space();
let extra = self.extra;
let (points, interact) = match region.direction() {
let (points, interact) = match ui.direction() {
Direction::Horizontal => {
let interact =
region.reserve_space(vec2(self.min_spacing, available_space.y), None);
let interact = ui.reserve_space(vec2(self.min_spacing, available_space.y), None);
(
vec![
pos2(interact.rect.center().x, interact.rect.top() - extra),
@ -402,8 +399,7 @@ impl Widget for Separator {
)
}
Direction::Vertical => {
let interact =
region.reserve_space(vec2(available_space.x, self.min_spacing), None);
let interact = ui.reserve_space(vec2(available_space.x, self.min_spacing), None);
(
vec![
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,
color: self.color,
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> {
fn ui(mut self, region: &mut Region) -> GuiResponse {
fn ui(mut self, ui: &mut Ui) -> GuiResponse {
let text_style = TextStyle::Button;
let font = &region.fonts()[text_style];
let font = &ui.fonts()[text_style];
if let Some(text) = &self.text {
if self.id.is_none() {
@ -116,35 +116,35 @@ impl<'a> Widget for Slider<'a> {
let slider_sans_text = Slider { text: None, ..self };
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 pos = region.reserve_space(text_size, None).rect.min;
region.add_text(pos, text_style, text, text_color);
slider_sans_text.ui(region)
let pos = ui.reserve_space(text_size, None).rect.min;
ui.add_text(pos, text_style, text, text_color);
slider_sans_text.ui(ui)
} else {
region.columns(2, |columns| {
ui.columns(2, |columns| {
// Slider on the left:
let slider_response = columns[0].add(slider_sans_text);
// Place the text in line with the slider on the left:
columns[1].set_desired_height(slider_response.rect.height());
columns[1].horizontal(|region| {
region.set_align(Align::Center);
region.add(Label::new(full_text).multiline(false));
columns[1].horizontal(|ui| {
ui.set_align(Align::Center);
ui.add(Label::new(full_text).multiline(false));
});
slider_response
})
}
} 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 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 {
x: region.available_width(),
x: ui.available_width(),
y: height,
},
Some(id),
@ -156,7 +156,7 @@ impl<'a> Widget for Slider<'a> {
let range = self.range.clone();
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 {
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 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(
pos2(interact.rect.left(), rect.center().y - rail_radius),
pos2(interact.rect.right(), rect.center().y + rail_radius),
);
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,
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
});
region.add_paint_cmd(PaintCmd::Circle {
ui.add_paint_cmd(PaintCmd::Circle {
center: pos2(marker_center_x, rail_rect.center().y),
radius: handle_radius,
fill_color: region.style().interact_fill_color(&interact),
fill_color: ui.style().interact_fill_color(&interact),
outline: Some(Outline::new(
region.style().interact_stroke_width(&interact),
region.style().interact_stroke_color(&interact),
ui.style().interact_stroke_width(&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> {
text: &'t mut String,
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>,
}
@ -35,29 +35,29 @@ impl<'t> TextEdit<'t> {
}
impl<'t> Widget for TextEdit<'t> {
fn ui(self, region: &mut Region) -> GuiResponse {
let id = region.make_child_id(self.id);
fn ui(self, ui: &mut Ui) -> GuiResponse {
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 (text, text_size) = font.layout_multiline(self.text.as_str(), region.available_width());
let desired_size = text_size.max(vec2(region.available_width(), line_spacing));
let interact = region.reserve_space(desired_size, Some(id));
let (text, text_size) = font.layout_multiline(self.text.as_str(), ui.available_width());
let desired_size = text_size.max(vec2(ui.available_width(), line_spacing));
let interact = ui.reserve_space(desired_size, Some(id));
if interact.clicked {
region.request_kb_focus(id);
ui.request_kb_focus(id);
}
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 {
for event in &region.input().events {
for event in &ui.input().events {
match event {
Event::Copy | Event::Cut => {
// TODO: cut
region.ctx().output().copied_text = self.text.clone();
ui.ctx().output().copied_text = self.text.clone();
}
Event::Text(text) => {
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,
corner_radius: 0.0,
// fill_color: Some(color::BLACK),
fill_color: region.style().interact_fill_color(&interact),
// fill_color: Some(region.style().background_fill_color()),
fill_color: ui.style().interact_fill_color(&interact),
// fill_color: Some(ui.style().background_fill_color()),
outline: None, //Some(Outline::new(1.0, color::WHITE)),
});
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 =
(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 {
let cursor_pos = if let Some(last) = text.last() {
interact.rect.min + vec2(last.max_x(), last.y_offset)
} else {
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)),
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();
ctx.begin_frame(raw_input.clone()); // TODO: avoid clone
let mut region = ctx.background_region();
let mut region = region.centered_column(region.available_width().min(480.0));
region.set_align(Align::Min);
region.add(label!("Emigui running inside of Glium").text_style(emigui::TextStyle::Heading));
if region.add(Button::new("Quit")).clicked {
let mut ui = ctx.fullscreen_ui();
let mut ui = ui.centered_column(ui.available_width().min(480.0));
ui.set_align(Align::Min);
ui.add(label!("Emigui running inside of Glium").text_style(emigui::TextStyle::Heading));
if ui.add(Button::new("Quit")).clicked {
running = false;
}
region.add(
ui.add(
label!(
"CPU usage: {:.2} ms (excludes painting)",
1e3 * frame_times.average().unwrap_or_default()
)
.text_style(TextStyle::Monospace),
);
region.add(
ui.add(
label!(
"FPS: {:.1}",
1.0 / frame_times.mean_time_interval().unwrap_or_default()
@ -98,15 +98,15 @@ fn main() {
.default_size(vec2(300.0, 600.0))
// .mutate(|w| w.resize = w.resize.auto_expand_width(true))
// .resize(|r| r.auto_expand_width(true))
.show(region.ctx(), |region| {
example_app.ui(region);
.show(ui.ctx(), |ui| {
example_app.ui(ui);
});
Window::new("Emigui settings")
.default_pos(pos2(450.0, 100.0))
.default_size(vec2(450.0, 500.0))
.show(region.ctx(), |region| {
ctx.ui(region);
.show(ui.ctx(), |ui| {
ctx.ui(ui);
});
let (output, paint_batches) = ctx.end_frame();

View file

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