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:
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>(
|
||||
|
||||
Reference in New Issue
Block a user