From 82350a2f1e66db0d2fa78f511fdd3667c4deba83 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 21 Feb 2021 10:12:08 +0100 Subject: [PATCH] Improve documentation --- ARCHITECTURE.md | 50 ++++++++++++++++++ CONTRIBUTING.md | 50 +++++++++++++++--- check.sh | 10 +++- egui/src/lib.rs | 84 ++++++++++++++++++++++++++++-- egui/src/widgets/mod.rs | 4 ++ egui/src/widgets/selected_label.rs | 2 +- egui_glium/src/painter.rs | 2 +- emath/src/rect.rs | 11 +++- start_server.sh | 10 +++- 9 files changed, 204 insertions(+), 19 deletions(-) create mode 100644 ARCHITECTURE.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..61656198a --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,50 @@ +# Arcitecture +This document describes how the crates that make up egui are all connected. + +Also see `CONTRIBUTING.md` for what to do before opening a PR. + + +## Crate overview +The crates in this repository are: `egui, emath, epaint, egui, epi, egui_web, egui_glium, egui_demo_lib, egui_demo_app`. + +### `egui`: The main GUI library. +Example code: `if ui.button("Click me").clicked() { … }` +This is the crate where the bulk of the code is at. `egui` depends only on `emath` and `epaint`. + +### `emath`: minimal 2D math library +Examples: `Vec2, Pos2, Rect, lerp, remap` + +### `epaint` +2d shapes and text that can be turned into textured triangles. + +Example: `Shape::Circle { center, radius, fill, stroke }` + +Depends on `emath`, [`rusttype`](https://crates.io/crates/rusttype), [`atomic_refcell`](https://crates.io/crates/atomic_refcell), [`ahash`](https://crates.io/crates/ahash). + +### `epi` +Depends only on `egui`. +Adds a thin application level wrapper around `egui` for hosting an `egui` app inside of `eframe`. + +### `egui_web` +Puts an egui app inside the web browser by compiling to WASM and binding to the web browser with [`js-sys`](https://crates.io/crates/js-sys) and [`wasm-bindgen`](https://crates.io/crates/wasm-bindgen). Paints the triangles that egui outputs using WebGL. + +### `egui_glium` +Puts an egui app inside a native window on your laptop. Paints the triangles that egui outputs using [glium](https://github.com/glium/glium). + +### `eframe` +A wrapper around `egui_web` + `egui_glium`, so you can compile the same app for either web or native. + +The demo that you can see at is using `eframe` to host the `egui`. The demo code is found in: + +### `egui_demo_lib` +Depends on `egui` + `epi`. +This contains a bunch of uses of `egui` and looks like the ui code you would write for an `egui` app. + + +### `egui_demo_app` +Thin wrapper around `egui_demo_lib` so we can compile it to a web site or a native app executable. +Depends on `egui_demo_lib` + `eframe`. + +### Other integrations + +There are also many great integrations for game engines such as `bevy` and `miniquad` which you can find at . diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5ed2facde..f4c25b831 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,26 +1,62 @@ # Contributing guidelines -You are welcome to contribute to egui via discussions, issues, pull requests and by publishing egui integrations. +## Introduction + +`egui` has been an on-and-off weekend project of mine since late 2018. I am grateful to any help I can get, but bare in mind that sometimes I can be slow to respond because I am busy with other things! + +/ Emil + ## Discussion -You can ask questions, share screenshots and more at GitHub Discussions: https://github.com/emilk/egui/discussions +You can ask questions, share screenshots and more at [GitHub Discussions](https://github.com/emilk/egui/discussions). + +There is also an `egui` channel on the Embark discord at (NOTE: I work at [Embark](https://www.embark-studios.com/), but `egui` is my hobby project). -There is also an egui channel on the Embark discord at (NOTE: I work at [Embark](https://www.embark-studios.com/), but egui is my hobby project). ## Filing an issue -Issues are for bug reports and feature requests. Issues are not for asking questions (use [Discussions](https://github.com/emilk/egui/discussions) for that). +[Issues](https://github.com/emilk/egui/issues) are for bug reports and feature requests. Issues are not for asking questions (use [Discussions](https://github.com/emilk/egui/discussions) for that). Always make sure there is not already a similar issue to the one you are creating. If you are filing a bug, please provide a way to reproduce it. + ## Making a PR -Always file an issue (or find an existing one) and get feedback before you start working on a non-trivial pull request! +First file an issue (or find an existing one) and announce that you plan to work on something. That way we will avoid having several people doing double work. Please ask for feedback before you start working on something non-trivial! + +Browse through [`ARCHITECTURE.md`](https://github.com/emilk/egui/blob/master/ARCHITECTURE.md) to get a sense of how all pieces connects. + +When you have something that works, open a draft PR. You may get some helpful feedback early! +When you feel the PR is ready to go, do a self-review of the code, and then open it for review. + ## Creating an integration for egui -If you make an integration for egui for some engine or renderer, please share it with the world! -I will add a link to it from the egui README.md so others can easily find it. +If you make an integration for `egui` for some engine or renderer, please share it with the world! +I will add a link to it from the `egui` README.md so others can easily find it. + +Read the section on integrations at . + + +## Code Conventions +Conventions unless otherwise specified: + +* angles are in radians +* `Vec2::X` is right and `Vec2::Y` is down. +* `Pos2::ZERO` is left top. + +While using an immediate mode gui is simple, implementing one is a lot more tricky. There are many subtle corner-case you need to think through. The `egui` source code is a bit messy, partially because it is still evolving. + +* read some code before writing your own +* follow the `egui` code style +* write idiomatic rust +* avoid `unsafe` +* avoid code that can cause panics +* use good names for everything +* add docstrings to types, `struct` fields and all `pub fn`. +* add some example code (doc-tests) +* before making a function longer, consider adding a helper function +* break the above rules when it makes sense diff --git a/check.sh b/check.sh index 767ec5bc0..1fa282036 100755 --- a/check.sh +++ b/check.sh @@ -1,19 +1,25 @@ #!/bin/bash set -eu +# Checks all tests, lints etc. +# Basically does what the CI does. + cargo check --workspace --all-targets +cargo test --workspace --doc cargo check --workspace --all-targets --all-features cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown cargo check -p egui_demo_app --lib --target wasm32-unknown-unknown --all-features -cargo fmt --all -- --check CARGO_INCREMENTAL=0 cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::all cargo test --workspace --all-targets --all-features -cargo test --workspace --doc +cargo fmt --all -- --check # TODO: doesn't error, but at least prints a warning. cargo doc -p emath -p epaint -p egui -p eframe -p epi -p egui_web -p egui_glium --lib --no-deps cargo doc -p egui_web --target wasm32-unknown-unknown --lib --no-deps +# ------------------------------------------------------------ +# + # For finding bloat: # cargo bloat --release --bin demo_glium -n 200 | rg egui diff --git a/egui/src/lib.rs b/egui/src/lib.rs index f9590ed94..092e6d9c7 100644 --- a/egui/src/lib.rs +++ b/egui/src/lib.rs @@ -1,8 +1,6 @@ -//! egui: an easy-to-use GUI in pure Rust +//! `egui`: an easy-to-use GUI in pure Rust! //! -//! Try the live web demo: . -//! -//! Read more about egui at . +//! Try the live web demo: . Read more about egui at . //! //! To quickly get started with egui, you can take a look at [`egui_template`](https://github.com/emilk/egui_template) //! which uses [`eframe`](https://docs.rs/eframe). @@ -45,6 +43,10 @@ //! //! To see what is possible to build we egui you can check out the online demo at . //! +//! If you like the "learning by doing" approach, clone and get started using egui right away. +//! +//! ### Getting a [`Ui`] +//! //! Use one of [`SidePanel`], [`TopPanel`], [`CentralPanel`], [`Window`] or [`Area`] to //! get access to an [`Ui`] where you can put widgets. For example: //! @@ -60,6 +62,80 @@ //! }); //! ``` //! +//! ## Conventions +//! +//! Conventions unless otherwise specified: +//! +//! * angles are in radians +//! * `Vec2::X` is right and `Vec2::Y` is down. +//! * `Pos2::ZERO` is left top. +//! +//! +//! ## Understanding immediate mode +//! +//! `egui` is an immediate mode GUI library. It is useful to fully grok what "immediate mode" implies. +//! +//! Here is an example to illustrate it: +//! +//! ``` +//! # let ui = &mut egui::Ui::__test(); +//! if ui.button("click me").clicked() { take_action() } +//! # fn take_action() {} +//! ``` +//! +//! This code is being executed each frame at maybe 60 frames per second. +//! Each frame egui does these things: +//! +//! * lays out the letters `click me` in order to figure out the size of the button +//! * decides where on screen to place the button +//! * check if the mouse is hovering or clicking that location +//! * chose button colors based on if it is being hovered or clicked +//! * add a [`Shape::Rect`] and [`Shape::Text`] to the list of shapes to be painted later this frame +//! * return a [`Response`] with the `clicked` member so the user can check for interactions +//! +//! There is no button being created and stored somewhere. +//! The only output of this call is some colored shapes, and a [`Response`]. +//! +//! Read more about the pros and cons of immediate mode at . +//! +//! ## How widgets works +//! +//! ``` +//! # let ui = &mut egui::Ui::__test(); +//! if ui.button("click me").clicked() { take_action() } +//! # fn take_action() {} +//! ``` +//! +//! is short for +//! +//! ``` +//! # let ui = &mut egui::Ui::__test(); +//! let button = egui::Button::new("click me"); +//! if ui.add(button).clicked() { take_action() } +//! # fn take_action() {} +//! ``` +//! +//! which is short for +//! +//! ``` +//! # use egui::Widget; +//! # let ui = &mut egui::Ui::__test(); +//! let button = egui::Button::new("click me"); +//! let response = button.ui(ui); +//! if response.clicked() { take_action() } +//! # fn take_action() {} +//! ``` +//! +//! [`Button`] uses the builder pattern to create the data required to show it. The [`Button`] is then discarded. +//! +//! [`Button`] implements `trait` [`Widget`], which looks like this: +//! ``` +//! # use egui::*; +//! pub trait Widget { +//! /// Allocate space, interact, paint, and return a [`Response`]. +//! fn ui(self, ui: &mut Ui) -> Response; +//! } +//! ``` //! //! # Code snippets //! diff --git a/egui/src/widgets/mod.rs b/egui/src/widgets/mod.rs index 60ca48eb5..935e179e3 100644 --- a/egui/src/widgets/mod.rs +++ b/egui/src/widgets/mod.rs @@ -29,6 +29,10 @@ pub use {button::*, drag_value::DragValue, image::Image, slider::*, text_edit::* // ---------------------------------------------------------------------------- /// Anything implementing Widget can be added to a [`Ui`] with [`Ui::add`]. +/// +/// Examples include `[Button]`, `[Label]` and [`Slider`]. +/// +/// `|ui: &mut Ui| -> Response { … }` also implemented `Widget`. #[must_use = "You should put this widget in an ui with `ui.add(widget);`"] pub trait Widget { /// Allocate space, interact, paint, and return a [`Response`]. diff --git a/egui/src/widgets/selected_label.rs b/egui/src/widgets/selected_label.rs index eca26a2f3..6519ef24c 100644 --- a/egui/src/widgets/selected_label.rs +++ b/egui/src/widgets/selected_label.rs @@ -1,7 +1,7 @@ use crate::*; /// One out of several alternatives, either selected or not. -/// Will mark selected items with a different background color +/// Will mark selected items with a different background color. /// An alternative to [`RadioButton`] and [`Checkbox`]. #[must_use = "You should put this widget in an ui with `ui.add(widget);`"] #[derive(Debug)] diff --git a/egui_glium/src/painter.rs b/egui_glium/src/painter.rs index 897451d87..cb2541834 100644 --- a/egui_glium/src/painter.rs +++ b/egui_glium/src/painter.rs @@ -200,7 +200,7 @@ impl Painter { let clip_max_x = pixels_per_point * clip_rect.max.x; let clip_max_y = pixels_per_point * clip_rect.max.y; - // Make sure clip rect can fit withing an `u32`: + // Make sure clip rect can fit within a `u32`: let clip_min_x = clamp(clip_min_x, 0.0..=width_in_pixels as f32); let clip_min_y = clamp(clip_min_y, 0.0..=height_in_pixels as f32); let clip_max_x = clamp(clip_max_x, clip_min_x..=width_in_pixels as f32); diff --git a/emath/src/rect.rs b/emath/src/rect.rs index a7a3caac2..da0d653eb 100644 --- a/emath/src/rect.rs +++ b/emath/src/rect.rs @@ -20,10 +20,17 @@ impl Rect { max: pos2(INFINITY, INFINITY), }; - /// The inverse of [`Self::EVERYTHING`]: streches from positive infinity to negative infinity. + /// The inverse of [`Self::EVERYTHING`]: stretches from positive infinity to negative infinity. /// Contains no points. /// - /// This is useful as the seed for boulding bounding boxes. + /// This is useful as the seed for bounding bounding boxes. + /// + /// ``` + /// # use emath::*; + /// let inf = f32::INFINITY; + /// assert!(Rect::NOTHING.size() == Vec2::splat(-inf)); + /// assert!(Rect::NOTHING.contains(pos2(0.0, 0.0)) == false); + /// ``` /// /// # Example: /// ``` diff --git a/start_server.sh b/start_server.sh index f8ea3383c..f5d1841d3 100755 --- a/start_server.sh +++ b/start_server.sh @@ -1,6 +1,12 @@ #!/bin/bash set -eu -cd docs +# Starts a local web-server that servs the contents of the `doc/` folder, +# i.e. the web-version of `egui_demo_app`. + +cargo install basic-http-server + echo "open http://localhost:8888" -python3 -m http.server 8888 --bind 127.0.0.1 + +(cd docs && basic-http-server --addr 127.0.0.1:8888 .) +# (cd docs && python3 -m http.server 8888 --bind 127.0.0.1)