[doc] improve Egui documentation

This commit is contained in:
Emil Ernerfeldt 2020-07-23 14:35:12 +02:00
parent 61cdec8fca
commit b79c76b9ce
17 changed files with 124 additions and 87 deletions

View file

@ -1,11 +1,11 @@
pub mod area; pub(crate) mod area;
pub mod collapsing_header; pub(crate) mod collapsing_header;
pub mod frame; pub(crate) mod frame;
pub mod menu; pub(crate) mod menu;
pub mod popup; pub(crate) mod popup;
pub mod resize; pub(crate) mod resize;
pub mod scroll_area; pub(crate) mod scroll_area;
pub mod window; pub(crate) mod window;
pub use { pub use {
area::Area, collapsing_header::CollapsingHeader, frame::Frame, popup::*, resize::Resize, area::Area, collapsing_header::CollapsingHeader, frame::Frame, popup::*, resize::Resize,

View file

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{widgets::*, *}; use crate::{paint::*, widgets::*, *};
use super::*; use super::*;

View file

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{color::*, containers::*, demos::FractalClock, widgets::*, *}; use crate::{color::*, containers::*, demos::FractalClock, paint::*, widgets::*, *};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -298,15 +298,9 @@ impl Widgets {
}); });
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.add(radio(self.radio == 0, "First")).clicked { ui.radio_value("First", &mut self.radio, 0);
self.radio = 0; ui.radio_value("Second", &mut self.radio, 1);
} ui.radio_value("Final", &mut self.radio, 2);
if ui.add(radio(self.radio == 1, "Second")).clicked {
self.radio = 1;
}
if ui.add(radio(self.radio == 2, "Final")).clicked {
self.radio = 2;
}
}); });
ui.add(Checkbox::new(&mut self.button_enabled, "Button enabled")); ui.add(Checkbox::new(&mut self.button_enabled, "Button enabled"));

View file

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{containers::*, widgets::*, *}; use crate::{containers::*, paint::PaintCmd, widgets::*, *};
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "with_serde", serde(default))] #[cfg_attr(feature = "with_serde", serde(default))]

View file

@ -1,34 +1,32 @@
//! Egui tracks widgets frame-to-frame using `Id`s. // TODO: have separate types `PositionId` and `UniqueId`. ?
//!
//! For instance, if you start dragging a slider one frame, egui stores
//! the sliders `Id` as the current active id so that next frame when
//! you move the mouse the same slider changes, even if the mouse has
//! moved outside the slider.
//!
//! For some widgets `Id`s are also used to persist some state about the
//! widgets, such as Window position or wether not a collapsing header region is open.
//!
//! This implicates that the `Id`s must be unqiue.
//!
//! For simple things like sliders and buttons that don't have any memory and
//! doesn't move we can use the location of the widget as a source of identity.
//! For instance, a slider only needs a unique and persistent ID while you are
//! dragging the slider. As long as it is still while moving, that is fine.
//!
//! 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 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.
//!
//! Then there are widgets that need no identifiers at all, like labels,
//! because they have no state nor are interacted with.
//!
//! So we have two type of Ids: `PositionId` and `UniqueId`.
//! TODO: have separate types for `PositionId` and `UniqueId`.
use std::hash::Hash; use std::hash::Hash;
/// Egui tracks widgets frame-to-frame using `Id`s.
///
/// For instance, if you start dragging a slider one frame, egui stores
/// the sliders `Id` as the current active id so that next frame when
/// you move the mouse the same slider changes, even if the mouse has
/// moved outside the slider.
///
/// For some widgets `Id`s are also used to persist some state about the
/// widgets, such as Window position or wether not a collapsing header region is open.
///
/// This implies that the `Id`s must be unqiue.
///
/// For simple things like sliders and buttons that don't have any memory and
/// doesn't move we can use the location of the widget as a source of identity.
/// For instance, a slider only needs a unique and persistent ID while you are
/// dragging the slider. As long as it is still while moving, that is fine.
///
/// 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 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.
///
/// Then there are widgets that need no identifiers at all, like labels,
/// because they have no state nor are interacted with.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Id(u64); pub struct Id(u64);

View file

@ -37,7 +37,7 @@ impl Layer {
/// Each `PaintCmd` is paired with a clip rectangle. /// Each `PaintCmd` is paired with a clip rectangle.
type PaintList = Vec<(Rect, PaintCmd)>; type PaintList = Vec<(Rect, PaintCmd)>;
/// TODO: improve this // TODO: improve this
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct GraphicLayers(AHashMap<Layer, PaintList>); pub struct GraphicLayers(AHashMap<Layer, PaintList>);

View file

@ -39,7 +39,7 @@ impl Default for Align {
/// Used e.g. to anchor a piece of text to a part of the rectangle. /// Used e.g. to anchor a piece of text to a part of the rectangle.
/// Give a position within the rect, specified by the aligns /// Give a position within the rect, specified by the aligns
pub fn align_rect(rect: Rect, align: (Align, Align)) -> Rect { pub(crate) fn align_rect(rect: Rect, align: (Align, Align)) -> Rect {
let x = match align.0 { let x = match align.0 {
Align::Min => rect.left(), Align::Min => rect.left(),
Align::Center => rect.left() - 0.5 * rect.width(), Align::Center => rect.left() - 0.5 * rect.width(),
@ -55,6 +55,7 @@ pub fn align_rect(rect: Rect, align: (Align, Align)) -> Rect {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// The layout of a `Ui`, e.g. horizontal left-aligned.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
// #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] // #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Layout { pub struct Layout {
@ -146,7 +147,7 @@ impl Layout {
/// 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.
/// ///
/// # How sizes are negotiated /// ## How sizes are negotiated
/// Each widget should have a *minimum desired size* and a *desired size*. /// Each widget should have a *minimum desired size* and a *desired size*.
/// When asking for space, ask AT LEAST for you minimum, and don't ask for more than you need. /// When asking for space, ask AT LEAST for you minimum, and don't ask for more than you need.
/// If you want to fill the space, ask about `available().size()` and use that. /// If you want to fill the space, ask about `available().size()` and use that.

View file

@ -50,7 +50,7 @@ pub use {
math::*, math::*,
memory::Memory, memory::Memory,
movement_tracker::MovementTracker, movement_tracker::MovementTracker,
paint::{color, Color, TextStyle, Texture}, paint::{color, Color, PaintJobs, TextStyle, Texture},
style::Style, style::Style,
types::*, types::*,
ui::Ui, ui::Ui,

View file

@ -1,5 +1,8 @@
use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, RangeInclusive, Sub, SubAssign}; use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, RangeInclusive, Sub, SubAssign};
/// A size or direction in 2D space.
///
/// Normally given in points, e.g. logical pixels.
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Vec2 { pub struct Vec2 {
@ -220,7 +223,10 @@ impl std::fmt::Debug for Vec2 {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// Sometimes called a Point. I prefer the shorter Pos2 so it is equal length to Vec2 // Sometimes called a Point. I prefer the shorter Pos2 so it is equal length to Vec2
/// A position on screen.
///
/// Normally given in points, e.g. logical pixels.
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Pos2 { pub struct Pos2 {
@ -353,6 +359,9 @@ impl std::fmt::Debug for Pos2 {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// A rectangular region of space.
///
/// Normally given in points, e.g. logical pixels.
#[derive(Clone, Copy, Default, Eq, PartialEq)] #[derive(Clone, Copy, Default, Eq, PartialEq)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Rect { pub struct Rect {
@ -594,6 +603,11 @@ pub fn ease_in_ease_out(t: f32) -> f32 {
3.0 * t * t - 2.0 * t * t * t 3.0 * t * t - 2.0 * t * t * t
} }
/// The circumference of a circle divided by its radius.
///
/// Represents one turn in radian angles. Equal to `2 * pi`.
///
/// See https://tauday.com/
pub const TAU: f32 = 2.0 * std::f32::consts::PI; pub const TAU: f32 = 2.0 * std::f32::consts::PI;
pub fn round_to_precision(value: f32, precision: usize) -> f32 { pub fn round_to_precision(value: f32, precision: usize) -> f32 {

View file

@ -6,6 +6,12 @@ use crate::{
Id, Layer, Pos2, Rect, Id, Layer, Pos2, Rect,
}; };
/// The data that Egui persists between frames.
///
/// This includes window positions and sizes,
/// how far the user has scrolled in a `ScrollArea` etc.
///
/// If you want this to persist when closing your app you should serialize `Memory` and store it.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "with_serde", serde(default))] #[cfg_attr(feature = "with_serde", serde(default))]
@ -30,7 +36,7 @@ pub struct Memory {
pub(crate) areas: Areas, pub(crate) areas: Areas,
} }
/// Say there is a butotn in a scroll area. /// Say there is a button in a scroll area.
/// If the user clicks the button, the button should click. /// If the user clicks the button, the button should click.
/// If the user drags the button we should scroll the scroll area. /// If the user drags the button we should scroll the scroll area.
/// So what we do is that when the mouse is pressed we register both the button /// So what we do is that when the mouse is pressed we register both the button

View file

@ -1,5 +1,5 @@
/// 0-255 `sRGBA`. TODO: rename `sRGBA` for clarity. // TODO: rename `Color` to `sRGBA` for clarity.
/// Uses premultiplied alpha. /// 0-255 `sRGBA`. Uses premultiplied alpha.
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Color { pub struct Color {

View file

@ -11,7 +11,7 @@ use super::{
texture_atlas::{Texture, TextureAtlas}, texture_atlas::{Texture, TextureAtlas},
}; };
/// TODO: rename // TODO: rename
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
// #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] // #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub enum TextStyle { pub enum TextStyle {

View file

@ -1,7 +1,8 @@
/// An 8-bit texture containing font data.
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Texture { pub struct Texture {
/// e.g. a hash of the data. Use this to detect changes! /// e.g. a hash of the data. Use this to detect changes!
pub id: u64, // TODO pub id: u64,
pub width: usize, pub width: usize,
pub height: usize, pub height: usize,
pub pixels: Vec<u8>, pub pixels: Vec<u8>,
@ -25,7 +26,9 @@ impl std::ops::IndexMut<(usize, usize)> for Texture {
} }
} }
/// A texture pixels, used for fonts. /// Contains font data in an atlas, where each character occupied a small rectangle.
///
/// More characters can be added, possibly expanding the texture.
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct TextureAtlas { pub struct TextureAtlas {
texture: Texture, texture: Texture,

View file

@ -3,6 +3,7 @@
use crate::{color::*, math::*, paint::LineStyle, types::*}; use crate::{color::*, math::*, paint::LineStyle, types::*};
// TODO: split into Spacing and Style? // TODO: split into Spacing and Style?
/// Specifies the look and feel of a `Ui`.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "with_serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Style { pub struct Style {

View file

@ -4,9 +4,12 @@ use crate::{math::Rect, Context, Ui};
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// What Egui emits each frame.
/// The backend should use this.
#[derive(Clone, Default)] #[derive(Clone, Default)]
// #[cfg_attr(feature = "with_serde", derive(serde::Serialize))] // #[cfg_attr(feature = "with_serde", derive(serde::Serialize))]
pub struct Output { pub struct Output {
/// Set the cursor to this icon.
pub cursor_icon: CursorIcon, pub cursor_icon: CursorIcon,
/// If set, open this url. /// If set, open this url.
@ -43,6 +46,9 @@ impl Default for CursorIcon {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/// The result of an interaction.
///
/// For instance, this lets you know whether or not a widget has been clicked this frame.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
// #[cfg_attr(feature = "with_serde", derive(serde::Serialize))] // #[cfg_attr(feature = "with_serde", derive(serde::Serialize))]
pub struct InteractInfo { pub struct InteractInfo {
@ -90,7 +96,10 @@ impl InteractInfo {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// TODO: rename GuiResponse /// The result of adding a widget to an `Ui`.
///
/// This lets you know whether or not a widget has been clicked this frame.
/// It also lets you easily show a tooltip on hover.
pub struct GuiResponse { pub struct GuiResponse {
/// The senses (click or drag) that the widget is interested in (if any). /// The senses (click or drag) that the widget is interested in (if any).
pub sense: Sense, pub sense: Sense,

View file

@ -112,7 +112,7 @@ impl Ui {
self.id self.id
} }
/// Options for this ui, and any child uis we may spawn. /// Style options for this `Ui` and its children.
pub fn style(&self) -> &Style { pub fn style(&self) -> &Style {
&self.style &self.style
} }
@ -279,9 +279,10 @@ impl Ui {
pub fn request_kb_focus(&self, id: Id) { pub fn request_kb_focus(&self, id: Id) {
self.memory().kb_focus_id = Some(id); self.memory().kb_focus_id = Some(id);
} }
}
// ------------------------------------------------------------------------ /// # `Id` creation
impl Ui {
/// 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 ui /// If the `id_source` is not unique within this ui
@ -328,10 +329,10 @@ impl Ui {
pub fn make_child_id(&self, id_seed: impl Hash) -> Id { pub fn make_child_id(&self, id_seed: impl Hash) -> Id {
self.id.with(id_seed) self.id.with(id_seed)
} }
}
// ------------------------------------------------------------------------ /// # Interaction
// Interaction impl Ui {
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> InteractInfo { pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> InteractInfo {
self.ctx self.ctx
.interact(self.layer, self.clip_rect, rect, Some(id), sense) .interact(self.layer, self.clip_rect, rect, Some(id), sense)
@ -374,7 +375,7 @@ impl 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.
/// ///
/// # How sizes are negotiated /// ## How sizes are negotiated
/// Each widget should have a *minimum desired size* and a *desired size*. /// Each widget should have a *minimum desired size* and a *desired size*.
/// When asking for space, ask AT LEAST for you minimum, and don't ask for more than you need. /// When asking for space, ask AT LEAST for you minimum, and don't ask for more than you need.
/// If you want to fill the space, ask about `available().size()` and use that. /// If you want to fill the space, ask about `available().size()` and use that.
@ -434,10 +435,10 @@ impl Ui {
self.child_count += 1; self.child_count += 1;
child_rect child_rect
} }
}
// ------------------------------------------------ /// # Painting related stuff
// Painting related stuff impl Ui {
/// It is up to the caller to make sure there is room for this. /// It is up to the caller to make sure there is room for this.
/// Can be used for free painting. /// Can be used for free painting.
/// NOTE: all coordinates are screen coordinates! /// NOTE: all coordinates are screen coordinates!
@ -524,39 +525,44 @@ impl Ui {
color, color,
}); });
} }
}
// ------------------------------------------------------------------------ /// # Adding widgets
// Addding Widgets impl Ui {
pub fn add(&mut self, widget: impl Widget) -> GuiResponse { pub fn add(&mut self, widget: impl Widget) -> GuiResponse {
let interact = widget.ui(self); let interact = widget.ui(self);
self.response(interact) self.response(interact)
} }
// Convenience functions: /// Shortcut for `add(Label::new(text))`
pub fn label(&mut self, label: impl Into<Label>) -> GuiResponse { pub fn label(&mut self, label: impl Into<Label>) -> GuiResponse {
self.add(label.into()) self.add(label.into())
} }
/// Shortcut for `add(Hyperlink::new(url))`
pub fn hyperlink(&mut self, url: impl Into<String>) -> GuiResponse { pub fn hyperlink(&mut self, url: impl Into<String>) -> GuiResponse {
self.add(Hyperlink::new(url)) self.add(Hyperlink::new(url))
} }
/// Shortcut for `add(Button::new(text))`
pub fn button(&mut self, text: impl Into<String>) -> GuiResponse { pub fn button(&mut self, text: impl Into<String>) -> GuiResponse {
self.add(Button::new(text)) self.add(Button::new(text))
} }
// Argument order matching that of Dear ImGui // Argument order matching that of Dear ImGui
/// Show a checkbox.
pub fn checkbox(&mut self, text: impl Into<String>, checked: &mut bool) -> GuiResponse { pub fn checkbox(&mut self, text: impl Into<String>, checked: &mut bool) -> GuiResponse {
self.add(Checkbox::new(checked, text)) self.add(Checkbox::new(checked, text))
} }
// Argument order matching that of Dear ImGui // Argument order matching that of Dear ImGui
/// Show a radio button.
pub fn radio(&mut self, text: impl Into<String>, checked: bool) -> GuiResponse { pub fn radio(&mut self, text: impl Into<String>, checked: bool) -> GuiResponse {
self.add(RadioButton::new(checked, text)) self.add(RadioButton::new(checked, text))
} }
/// Show a radio button. It is selected if `*curr_value == radio_value`.
/// If clicked, `radio_value` is assigned to `*curr_value`;
pub fn radio_value<Value: PartialEq>( pub fn radio_value<Value: PartialEq>(
&mut self, &mut self,
text: impl Into<String>, text: impl Into<String>,
@ -570,13 +576,14 @@ impl Ui {
response response
} }
/// Shortcut for `add(Separator::new())`
pub fn separator(&mut self) -> GuiResponse { pub fn separator(&mut self) -> GuiResponse {
self.add(Separator::new()) self.add(Separator::new())
} }
}
// ------------------------------------------------------------------------ /// # Addding Containers / Sub-uis:
// Addding Containers / Sub-uis: impl Ui {
pub fn collapsing<R>( pub fn collapsing<R>(
&mut self, &mut self,
text: impl Into<String>, text: impl Into<String>,
@ -737,6 +744,4 @@ impl Ui {
self.allocate_space(size); self.allocate_space(size);
result result
} }
// ------------------------------------------------
} }

View file

@ -1,11 +1,19 @@
//! Example widget uses:
//!
//! * `ui.add(Label::new("Text").text_color(color::red));`
//!
//! * `if ui.add(Button::new("Click me")).clicked { ... }`
#![allow(clippy::new_without_default)] #![allow(clippy::new_without_default)]
use crate::{layout::Direction, *}; use crate::{layout::Direction, *};
mod slider; mod slider;
pub mod text_edit; pub(crate) mod text_edit;
pub use {paint::*, slider::*, text_edit::*}; pub use {slider::*, text_edit::*};
use paint::*;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -98,7 +106,9 @@ impl Label {
} }
} }
/// Usage: label!("Foo: {}", bar) /// Shortcut for creating a `Label` widget.
///
/// Usage: `label!("Foo: {}", bar)` equivalent to `Label::new(format!("Foo: {}", bar))`.
#[macro_export] #[macro_export]
macro_rules! label { macro_rules! label {
($fmt:expr) => ($crate::widgets::Label::new($fmt)); ($fmt:expr) => ($crate::widgets::Label::new($fmt));
@ -382,10 +392,6 @@ impl RadioButton {
} }
} }
pub fn radio(checked: bool, text: impl Into<String>) -> RadioButton {
RadioButton::new(checked, text)
}
impl Widget for RadioButton { impl Widget for RadioButton {
fn ui(self, ui: &mut Ui) -> InteractInfo { fn ui(self, ui: &mut Ui) -> InteractInfo {
let RadioButton { let RadioButton {