1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

[docs] Improve README.md and documentation

This commit is contained in:
Emil Ernerfeldt
2020-08-21 18:53:43 +02:00
parent ad9783a33d
commit 73cea29f7d
15 changed files with 366 additions and 189 deletions

View File

@@ -5,8 +5,8 @@
use crate::Ui;
/// Implement this trait to write apps that can be compiled both natively using the `egui_glium` crate,
/// and deployed as a web site using the `egui_web` crate.
/// Implement this trait to write apps that can be compiled both natively using the [`egui_glium`](https://crates.io/crates/egui_glium) crate,
/// and deployed as a web site using the [`egui_web`](https://crates.io/crates/egui_web) crate.
pub trait App {
/// Called each time the UI needs repainting, which may be many times per second.
fn ui(&mut self, ui: &mut Ui, backend: &mut dyn Backend);
@@ -19,7 +19,7 @@ pub trait App {
/// How the backend runs the app
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RunMode {
/// Rapint the UI all the time (at the display refresh rate of e.g. 60 Hz).
/// Repint the UI all the time (at the display refresh rate of e.g. 60 Hz).
/// This is good for games where things are constantly moving.
/// This can also be achieved with `RunMode::Reactive` combined with calling `egui::Context::request_repaint()` each frame.
Continuous,

View File

@@ -1,4 +1,4 @@
//! Containers are pieces of the UI which wraps other pieces of UI.
//! Containers are pieces of the UI which wraps other pieces of UI. Examples: `Window`, `ScrollArea`, `Resize`, etc.
//!
//! For instance, a `Frame` adds a frame and background to some contained UI.

View File

@@ -4,7 +4,14 @@ use crate::{paint::*, widgets::*, *};
use super::*;
/// A floating window which can be moved, closed, collapsed, resized and scrolled.
/// Builder for a floating window which can be dragged, closed, collapsed, resized and scrolled.
///
/// You can customize:
/// * title
/// * default, minimum, maximum and/or fixed size
/// * if the window has a scroll area
/// * if the window can be collapsed (minimized) to just the title bar
/// * if there should be a close button
pub struct Window<'open> {
pub title_label: Label,
open: Option<&'open mut bool>,
@@ -41,71 +48,79 @@ impl<'open> Window<'open> {
}
}
/// If the given bool is false, the window will not be visible.
/// If the given bool is true, the window will have a close button that sets this bool to false.
/// Call this to add a close-button to the window title bar.
///
/// * If `*open == false`, the window will not be visible.
/// * If `*open == true`, the window will have a close button.
/// * If the close button is pressed, `*open` will be set to `false`.
pub fn open(mut self, open: &'open mut bool) -> Self {
self.open = Some(open);
self
}
/// Usage: `Winmdow::new(...).mutate(|w| w.resize = w.resize.auto_expand_width(true))`
/// Usage: `Window::new(...).mutate(|w| w.resize = w.resize.auto_expand_width(true))`
/// Not sure this is a good interface for this.
pub fn mutate(mut self, mutate: impl Fn(&mut Self)) -> Self {
mutate(&mut self);
self
}
/// Usage: `Winmdow::new(...).resize(|r| r.auto_expand_width(true))`
/// Usage: `Window::new(...).resize(|r| r.auto_expand_width(true))`
/// Not sure this is a good interface for this.
pub fn resize(mut self, mutate: impl Fn(Resize) -> Resize) -> Self {
self.resize = mutate(self.resize);
self
}
/// Usage: `Winmdow::new(...).frame(|f| f.fill(Some(BLUE)))`
/// Usage: `Window::new(...).frame(|f| f.fill(Some(BLUE)))`
/// Not sure this is a good interface for this.
pub fn frame(mut self, frame: Frame) -> Self {
self.frame = Some(frame);
self
}
/// Set initial position of the window.
pub fn default_pos(mut self, default_pos: impl Into<Pos2>) -> Self {
self.area = self.area.default_pos(default_pos);
self
}
/// Set initial size of the window.
pub fn default_size(mut self, default_size: impl Into<Vec2>) -> Self {
self.resize = self.resize.default_size(default_size);
self
}
/// Set initial width of the window.
pub fn default_width(mut self, default_width: f32) -> Self {
self.resize = self.resize.default_width(default_width);
self
}
/// Set initial height of the window.
pub fn default_height(mut self, default_height: f32) -> Self {
self.resize = self.resize.default_height(default_height);
self
}
/// Set initial position and size of the window.
pub fn default_rect(self, rect: Rect) -> Self {
self.default_pos(rect.min).default_size(rect.size())
}
/// Positions the window and prevents it from being moved
/// Sets the window position and prevents it from being dragged around.
pub fn fixed_pos(mut self, pos: impl Into<Pos2>) -> Self {
self.area = self.area.fixed_pos(pos);
self
}
/// Sets the window size and prevents it from being resized by dragging its edges.
pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {
self.resize = self.resize.fixed_size(size);
self
}
/// Can you resize it with the mouse?
/// Note that a window can still auto-resize
/// Can the user resize the window by dragging its edges?
/// Note that even if you set this to `false` the window may still auto-resize.
pub fn resizable(mut self, resizable: bool) -> Self {
self.resize = self.resize.resizable(resizable);
self
@@ -118,14 +133,21 @@ impl<'open> Window<'open> {
}
/// Not resizable, just takes the size of its contents.
/// Also disabled scrolling.
pub fn auto_sized(mut self) -> Self {
self.resize = self.resize.auto_sized();
self.scroll = None;
self
}
/// Enable/disable scrolling. True by default.
pub fn scroll(mut self, scroll: bool) -> Self {
if !scroll {
if scroll {
debug_assert!(
self.scroll.is_some(),
"Window::scroll called multiple times"
);
} else {
self.scroll = None;
}
self

View File

@@ -194,7 +194,7 @@ impl Context {
let paint_commands = self.drain_paint_lists();
let num_primitives = paint_commands.len();
let paint_jobs =
tessellator::tessellate_paint_commands(paint_options, self.fonts(), paint_commands);
tessellator::tessellate_paint_commands(paint_commands, paint_options, self.fonts());
{
let mut stats = PaintStats::default();

View File

@@ -1,3 +1,6 @@
//! Demo-code for showing how Egui is used.
//!
//! The demo-code is also used in benchmarks and tests.
mod app;
mod fractal_clock;

View File

@@ -4,6 +4,10 @@ use crate::{app, color::*, containers::*, demos::FractalClock, paint::*, widgets
// ----------------------------------------------------------------------------
/// Demonstrates how to make an app using Egui.
///
/// Implements `App` so it can be used with
/// [`egui_glium`](https://crates.io/crates/egui_glium) and [`egui_web`](https://crates.io/crates/egui_web).
#[derive(Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))]
@@ -16,7 +20,9 @@ pub struct DemoApp {
}
impl DemoApp {
/// `web_location_hash`: for web demo only. e.g. "#fragmet".
/// Show the app ui (menu bar and windows).
///
/// * `web_location_hash`: for web demo only. e.g. "#fragment". Set to "".
pub fn ui(&mut self, ui: &mut Ui, web_location_hash: &str) {
if self.previous_web_location_hash != web_location_hash {
// #fragment end of URL:
@@ -34,6 +40,7 @@ impl DemoApp {
self.windows(ui.ctx());
}
/// Show the open windows.
pub fn windows(&mut self, ctx: &Arc<Context>) {
let DemoApp {
open_windows,

View File

@@ -6,7 +6,8 @@ const MAX_CLICK_DIST: f32 = 6.0;
const MAX_CLICK_DELAY: f64 = 0.3;
/// What the integration gives to the gui.
/// All coordinates in egui is in point/logical coordinates.
/// All coordinates in egui is in point/logical coordinates
/// with origin (0, 0) in the top left corner.
#[derive(Clone, Debug, Default)]
pub struct RawInput {
/// Is the button currently down?
@@ -19,13 +20,13 @@ pub struct RawInput {
pub scroll_delta: Vec2,
/// Size of the screen in points.
/// TODO: this should be screen_rect for easy sandboxing.
// TODO: this should be screen_rect for easy sandboxing.
pub screen_size: Vec2,
/// Also known as device pixel ratio, > 1 for HDPI screens.
pub pixels_per_point: Option<f32>,
/// Time in seconds. Relative to whatever. Used for animation.
/// Time in seconds. Relative to whatever. Used for animations.
pub time: f64,
/// Local time. Only used for the clock in the demo app.

View File

@@ -1,4 +1,26 @@
#![deny(warnings)]
//! Egui core library
//!
//! To get started with Egui, you can use one of the available backends
//! such as [`egui_web`](https://crates.io/crates/egui_web) or [`egui_glium`](https://crates.io/crates/egui_glium).
//!
//! To write your own backend for Egui you need to do this:
//!
//! ``` ignore
//! let mut egui_ctx = egui::Context::new();
//!
//! // game loop:
//! loop {
//! let raw_input: egui::RawInput = my_backend.gather_input();
//! let mut ui = egui_ctx.begin_frame(raw_input);
//! my_app.ui(&mut ui); // add windows and widgets to `ui` here
//! let (output, paint_jobs) = egui_ctx.end_frame();
//! my_backend.paint(paint_jobs);
//! my_backend.set_cursor_icon(output.cursor_icon);
//! // Also see `egui::Output` for more
//! }
//! ```
// #![deny(warnings)] // TODO: remove
#![warn(
clippy::all,
clippy::dbg_macro,

View File

@@ -1,3 +1,8 @@
//! Converts graphics primitives into textured triangles.
//!
//! This module converts lines, circles, text and more represented by `PaintCmd`
//! into textured triangles represented by `Triangles`.
#![allow(clippy::identity_op)]
use {
@@ -12,17 +17,22 @@ use {
/// The UV coordinate of a white region of the texture mesh.
const WHITE_UV: (u16, u16) = (1, 1);
/// The vertex type.
///
/// Should be friendly to send to GPU as is.
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct Vertex {
/// Logical pixel coordinates (points)
pub pos: Pos2,
/// Logical pixel coordinates (points).
/// (0,0) is the top left corner of the screen.
pub pos: Pos2, // 64 bit
/// Texel coordinates in the texture
pub uv: (u16, u16),
pub uv: (u16, u16), // 32 bit
/// sRGBA with premultiplied alpha
pub color: Color,
pub color: Color, // 32 bit
}
/// Textured triangles
/// Textured triangles.
#[derive(Clone, Debug, Default)]
pub struct Triangles {
/// Draw as triangles (i.e. the length is always multiple of three).
@@ -39,36 +49,49 @@ pub type PaintJobs = Vec<PaintJob>;
// ----------------------------------------------------------------------------
/// ## Helpers for adding
impl Triangles {
pub fn append(&mut self, triangles: &Triangles) {
let index_offset = self.vertices.len() as u32;
for index in &triangles.indices {
self.indices.push(index_offset + index);
}
self.vertices.extend(triangles.vertices.iter());
/// Are all indices within the bounds of the contained vertices?
pub fn is_valid(&self) -> bool {
let n = self.vertices.len() as u32;
self.indices.iter().all(|&i| i < n)
}
fn triangle(&mut self, a: u32, b: u32, c: u32) {
/// Append all the indices and vertices of `other` to `self`.
pub fn append(&mut self, other: &Triangles) {
let index_offset = self.vertices.len() as u32;
for index in &other.indices {
self.indices.push(index_offset + index);
}
self.vertices.extend(other.vertices.iter());
}
/// Add a triangle.
pub fn add_triangle(&mut self, a: u32, b: u32, c: u32) {
self.indices.push(a);
self.indices.push(b);
self.indices.push(c);
}
/// Make room for this many additional triangles (will reserve 3x as many indices).
/// See also `reserve_vertices`.
pub fn reserve_triangles(&mut self, additional_triangles: usize) {
self.indices.reserve(3 * additional_triangles);
}
/// Make room for this many additional vertices.
/// See also `reserve_triangles`.
pub fn reserve_vertices(&mut self, additional: usize) {
self.vertices.reserve(additional);
}
/// Uniformly colored rectangle
/// Uniformly colored rectangle.
pub fn add_rect(&mut self, top_left: Vertex, bottom_right: Vertex) {
debug_assert_eq!(top_left.color, bottom_right.color);
let idx = self.vertices.len() as u32;
self.triangle(idx + 0, idx + 1, idx + 2);
self.triangle(idx + 2, idx + 1, idx + 3);
self.add_triangle(idx + 0, idx + 1, idx + 2);
self.add_triangle(idx + 2, idx + 1, idx + 3);
let top_right = Vertex {
pos: pos2(bottom_right.pos.x, top_left.pos.y),
@@ -87,7 +110,8 @@ impl Triangles {
}
/// This is for platforms that only support 16-bit index buffers.
/// Splits this mesh into many small if needed.
///
/// Splits this mesh into many smaller meshes (if needed).
/// All the returned meshes will have indices that fit into a `u16`.
pub fn split_to_u16(self) -> Vec<Triangles> {
const MAX_SIZE: u32 = 1 << 16;
@@ -146,15 +170,20 @@ impl Triangles {
pub struct PathPoint {
pos: Pos2,
/// For filled paths the normal is used for antialiasing.
/// For outlines the normal is used for figuring out how to make the line wide
/// For filled paths the normal is used for anti-aliasing (both outlines and filled areas).
///
/// For outlines the normal is also used for giving thickness to the path
/// (i.e. in what direction to expand).
///
/// The normal could be estimated by differences between successive points,
/// but that would be less accurate (and in some cases slower).
///
/// Normals are normally unit-length.
normal: Vec2,
}
/// A 2D path that can be tesselated into triangles.
/// A connected line (without thickness or gaps) which can be tesselated
/// to either to an outline (with thickness) or a filled convex area.
#[derive(Clone, Debug, Default)]
pub struct Path(Vec<PathPoint>);
@@ -277,17 +306,24 @@ impl Path {
}
}
/// with x right, and y down (GUI coords) we have:
/// angle = dir
/// 0 * TAU / 4 = right
/// quadrant 0, right bottom
/// 1 * TAU / 4 = bottom
/// quadrant 1, left bottom
/// 2 * TAU / 4 = left
/// quadrant 2 left top
/// 3 * TAU / 4 = top
/// quadrant 3 right top
/// 4 * TAU / 4 = right
/// Add one quadrant of a circle
///
/// * quadrant 0: right bottom
/// * quadrant 1: left bottom
/// * quadrant 2: left top
/// * quadrant 3: right top
//
// Derivation:
//
// * angle 0 * TAU / 4 = right
// - quadrant 0: right bottom
// * angle 1 * TAU / 4 = bottom
// - quadrant 1: left bottom
// * angle 2 * TAU / 4 = left
// - quadrant 2: left top
// * angle 3 * TAU / 4 = top
// - quadrant 3: right top
// * angle 4 * TAU / 4 = right
pub fn add_circle_quadrant(&mut self, center: Pos2, radius: f32, quadrant: f32) {
// TODO: optimize with precalculated vertices for some radii ranges
@@ -316,6 +352,7 @@ pub enum PathType {
}
use self::PathType::{Closed, Open};
/// Tesselation quality options
#[derive(Clone, Copy)]
pub struct PaintOptions {
/// Anti-aliasing makes shapes appear smoother, but requires more triangles and is therefore slower.
@@ -336,11 +373,12 @@ impl Default for PaintOptions {
}
}
/// Tesselate the given convex area into a polygon.
pub fn fill_closed_path(
triangles: &mut Triangles,
options: PaintOptions,
path: &[PathPoint],
color: Color,
options: PaintOptions,
out: &mut Triangles,
) {
if color == color::TRANSPARENT {
return;
@@ -353,49 +391,48 @@ pub fn fill_closed_path(
color,
};
if options.anti_alias {
triangles.reserve_triangles(3 * n as usize);
triangles.reserve_vertices(2 * n as usize);
out.reserve_triangles(3 * n as usize);
out.reserve_vertices(2 * n as usize);
let color_outer = color::TRANSPARENT;
let idx_inner = triangles.vertices.len() as u32;
let idx_inner = out.vertices.len() as u32;
let idx_outer = idx_inner + 1;
for i in 2..n {
triangles.triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);
out.add_triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);
}
let mut i0 = n - 1;
for i1 in 0..n {
let p1 = &path[i1 as usize];
let dm = p1.normal * options.aa_size * 0.5;
triangles.vertices.push(vert(p1.pos - dm, color));
triangles.vertices.push(vert(p1.pos + dm, color_outer));
triangles.triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);
triangles.triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);
out.vertices.push(vert(p1.pos - dm, color));
out.vertices.push(vert(p1.pos + dm, color_outer));
out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);
out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);
i0 = i1;
}
} else {
triangles.reserve_triangles(n as usize);
let idx = triangles.vertices.len() as u32;
triangles
.vertices
.extend(path.iter().map(|p| vert(p.pos, color)));
out.reserve_triangles(n as usize);
let idx = out.vertices.len() as u32;
out.vertices.extend(path.iter().map(|p| vert(p.pos, color)));
for i in 2..n {
triangles.triangle(idx, idx + i - 1, idx + i);
out.add_triangle(idx, idx + i - 1, idx + i);
}
}
}
/// Tesselate the given path as an outline with thickness.
pub fn paint_path_outline(
triangles: &mut Triangles,
options: PaintOptions,
path_type: PathType,
path: &[PathPoint],
path_type: PathType,
style: LineStyle,
options: PaintOptions,
out: &mut Triangles,
) {
if style.color == color::TRANSPARENT {
return;
}
let n = path.len() as u32;
let idx = triangles.vertices.len() as u32;
let idx = out.vertices.len() as u32;
let vert = |pos, color| Vertex {
pos,
@@ -422,8 +459,8 @@ pub fn paint_path_outline(
return;
}
triangles.reserve_triangles(4 * n as usize);
triangles.reserve_vertices(3 * n as usize);
out.reserve_triangles(4 * n as usize);
out.reserve_vertices(3 * n as usize);
let mut i0 = n - 1;
for i1 in 0..n {
@@ -431,20 +468,18 @@ pub fn paint_path_outline(
let p1 = &path[i1 as usize];
let p = p1.pos;
let n = p1.normal;
triangles
.vertices
out.vertices
.push(vert(p + n * options.aa_size, color_outer));
triangles.vertices.push(vert(p, color_inner));
triangles
.vertices
out.vertices.push(vert(p, color_inner));
out.vertices
.push(vert(p - n * options.aa_size, color_outer));
if connect_with_previous {
triangles.triangle(idx + 3 * i0 + 0, idx + 3 * i0 + 1, idx + 3 * i1 + 0);
triangles.triangle(idx + 3 * i0 + 1, idx + 3 * i1 + 0, idx + 3 * i1 + 1);
out.add_triangle(idx + 3 * i0 + 0, idx + 3 * i0 + 1, idx + 3 * i1 + 0);
out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i1 + 0, idx + 3 * i1 + 1);
triangles.triangle(idx + 3 * i0 + 1, idx + 3 * i0 + 2, idx + 3 * i1 + 1);
triangles.triangle(idx + 3 * i0 + 2, idx + 3 * i1 + 1, idx + 3 * i1 + 2);
out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i0 + 2, idx + 3 * i1 + 1);
out.add_triangle(idx + 3 * i0 + 2, idx + 3 * i1 + 1, idx + 3 * i1 + 2);
}
i0 = i1;
}
@@ -462,8 +497,8 @@ pub fn paint_path_outline(
. |-----| inner_rad
*/
triangles.reserve_triangles(6 * n as usize);
triangles.reserve_vertices(4 * n as usize);
out.reserve_triangles(6 * n as usize);
out.reserve_vertices(4 * n as usize);
let mut i0 = n - 1;
for i1 in 0..n {
@@ -473,44 +508,36 @@ pub fn paint_path_outline(
let p1 = &path[i1 as usize];
let p = p1.pos;
let n = p1.normal;
triangles
.vertices
.push(vert(p + n * outer_rad, color_outer));
triangles
.vertices
.push(vert(p + n * inner_rad, color_inner));
triangles
.vertices
.push(vert(p - n * inner_rad, color_inner));
triangles
.vertices
.push(vert(p - n * outer_rad, color_outer));
out.vertices.push(vert(p + n * outer_rad, color_outer));
out.vertices.push(vert(p + n * inner_rad, color_inner));
out.vertices.push(vert(p - n * inner_rad, color_inner));
out.vertices.push(vert(p - n * outer_rad, color_outer));
if connect_with_previous {
triangles.triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);
triangles.triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);
out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);
out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);
triangles.triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);
triangles.triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);
out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
triangles.triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);
triangles.triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);
out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
}
i0 = i1;
}
}
} else {
triangles.reserve_triangles(2 * n as usize);
triangles.reserve_vertices(2 * n as usize);
out.reserve_triangles(2 * n as usize);
out.reserve_vertices(2 * n as usize);
let last_index = if path_type == Closed { n } else { n - 1 };
for i in 0..last_index {
triangles.triangle(
out.add_triangle(
idx + (2 * i + 0) % (2 * n),
idx + (2 * i + 1) % (2 * n),
idx + (2 * i + 2) % (2 * n),
);
triangles.triangle(
out.add_triangle(
idx + (2 * i + 2) % (2 * n),
idx + (2 * i + 1) % (2 * n),
idx + (2 * i + 3) % (2 * n),
@@ -526,21 +553,15 @@ pub fn paint_path_outline(
return;
}
for p in path {
triangles
.vertices
.push(vert(p.pos + radius * p.normal, color));
triangles
.vertices
.push(vert(p.pos - radius * p.normal, color));
out.vertices.push(vert(p.pos + radius * p.normal, color));
out.vertices.push(vert(p.pos - radius * p.normal, color));
}
} else {
let radius = style.width / 2.0;
for p in path {
triangles
.vertices
out.vertices
.push(vert(p.pos + radius * p.normal, style.color));
triangles
.vertices
out.vertices
.push(vert(p.pos - radius * p.normal, style.color));
}
}
@@ -560,32 +581,39 @@ fn mul_color(color: Color, factor: f32) -> Color {
// ----------------------------------------------------------------------------
/// `reused_path`: only used to reuse memory
/// Tesselate a single `PaintCmd` into a `Triangles`.
///
/// * `command`: the command to tesselate
/// * `options`: tesselation quality
/// * `fonts`: font source when tesselating text
/// * `out`: where the triangles are put
/// * `scratchpad_path`: if you plan to run `tessellate_paint_command`
/// many times, pass it a reference to the same `Path` to avoid excessive allocations.
pub fn tessellate_paint_command(
reused_path: &mut Path,
command: PaintCmd,
options: PaintOptions,
fonts: &Fonts,
command: PaintCmd,
out: &mut Triangles,
scratchpad_path: &mut Path,
) {
let path = reused_path;
let path = scratchpad_path;
path.clear();
match command {
PaintCmd::Noop => {}
PaintCmd::Circle {
center,
radius,
fill,
outline,
radius,
} => {
if radius > 0.0 {
path.add_circle(center, radius);
if let Some(fill) = fill {
fill_closed_path(out, options, &path.0, fill);
fill_closed_path(&path.0, fill, options, out);
}
if let Some(outline) = outline {
paint_path_outline(out, options, Closed, &path.0, outline);
if let Some(line_style) = outline {
paint_path_outline(&path.0, Closed, line_style, options, out);
}
}
}
@@ -594,7 +622,7 @@ pub fn tessellate_paint_command(
}
PaintCmd::LineSegment { points, style } => {
path.add_line_segment(points);
paint_path_outline(out, options, Open, &path.0, style);
paint_path_outline(&path.0, Open, style, options, out);
}
PaintCmd::Path {
path,
@@ -608,19 +636,19 @@ pub fn tessellate_paint_command(
closed,
"You asked to fill a path that is not closed. That makes no sense."
);
fill_closed_path(out, options, &path.0, fill);
fill_closed_path(&path.0, fill, options, out);
}
if let Some(outline) = outline {
if let Some(line_style) = outline {
let typ = if closed { Closed } else { Open };
paint_path_outline(out, options, typ, &path.0, outline);
paint_path_outline(&path.0, typ, line_style, options, out);
}
}
}
PaintCmd::Rect {
mut rect,
corner_radius,
fill,
outline,
mut rect,
} => {
if !rect.is_empty() {
// It is common to (sometimes accidentally) create an infinitely sized ractangle.
@@ -630,10 +658,10 @@ pub fn tessellate_paint_command(
path.add_rounded_rectangle(rect, corner_radius);
if let Some(fill) = fill {
fill_closed_path(out, options, &path.0, fill);
fill_closed_path(&path.0, fill, options, out);
}
if let Some(outline) = outline {
paint_path_outline(out, options, Closed, &path.0, outline);
if let Some(line_style) = outline {
paint_path_outline(&path.0, Closed, line_style, options, out);
}
}
}
@@ -678,13 +706,23 @@ pub fn tessellate_paint_command(
}
}
/// Turns `PaintCmd`:s into sets of triangles
/// Turns `PaintCmd`:s into sets of triangles.
///
/// The given commands will be painted back-to-front (painters algorithm).
/// They will be batched together by clip rectangle.
///
/// * `commands`: the command to tesselate
/// * `options`: tesselation quality
/// * `fonts`: font source when tesselating text
///
/// ## Returns
/// A list of clip rectangles with matching `Triangles`.
pub fn tessellate_paint_commands(
commands: Vec<(Rect, PaintCmd)>,
options: PaintOptions,
fonts: &Fonts,
commands: Vec<(Rect, PaintCmd)>,
) -> Vec<(Rect, Triangles)> {
let mut reused_path = Path::default();
let mut scratchpad_path = Path::default();
let mut jobs = PaintJobs::default();
for (clip_rect, cmd) in commands {
@@ -695,22 +733,22 @@ pub fn tessellate_paint_commands(
}
let out = &mut jobs.last_mut().unwrap().1;
tessellate_paint_command(&mut reused_path, options, fonts, cmd, out);
tessellate_paint_command(cmd, options, fonts, out, &mut scratchpad_path);
}
if options.debug_paint_clip_rects {
for (clip_rect, triangles) in &mut jobs {
tessellate_paint_command(
&mut reused_path,
options,
fonts,
PaintCmd::Rect {
rect: *clip_rect,
corner_radius: 0.0,
fill: None,
outline: Some(LineStyle::new(2.0, srgba(150, 255, 150, 255))),
},
options,
fonts,
triangles,
&mut scratchpad_path,
)
}
}

View File

@@ -472,6 +472,10 @@ impl Ui {
self.add(RadioButton::new(checked, text))
}
pub fn text_edit(&mut self, text: &mut String) -> GuiResponse {
self.add(TextEdit::new(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>(