diff --git a/README.md b/README.md index 0820400e250..1e683f68cad 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ egui is a simple, fast, and highly portable immediate mode GUI library for Rust. egui runs on the web, natively, and [in your favorite game engine](#integrations) (or will soon). -egui aims to be the easiest-to-use Rust GUI libary, and the simplest way to make a web app in Rust. +egui aims to be the easiest-to-use Rust GUI library, and the simplest way to make a web app in Rust. egui can be used anywhere you can draw textured triangles, which means you can easily integrate it into your game engine of choice. @@ -68,7 +68,7 @@ ui.label(format!("Hello '{}', age {}", name, age)); ## Goals -* The easiest to use GUI libary +* The easiest to use GUI library * Responsive: target 60 Hz in debug build * Friendly: difficult to make mistakes, and shouldn't panic * Portable: the same code works on the web and as a native app @@ -79,7 +79,7 @@ ui.label(format!("Hello '{}', age {}", name, age)); * Extensible: [easy to write your own widgets for egui](https://github.com/emilk/egui/blob/master/egui_demo_lib/src/apps/demo/toggle_switch.rs) * Modular: You should be able to use small parts of egui and combine them in new ways * Safe: there is no `unsafe` code in egui -* Minimal dependencies: [`ahash`](https://crates.io/crates/ahash) [`atomic_refcell`](https://crates.io/crates/atomic_refcell) [`ordered-float`](https://crates.io/crates/) [`rusttype`](https://crates.io/crates/rusttype). +* Minimal dependencies: [`ahash`](https://crates.io/crates/ahash) [`atomic_refcell`](https://crates.io/crates/atomic_refcell) [`ordered-float`](https://crates.io/crates/ordered-float) [`rusttype`](https://crates.io/crates/rusttype). egui is *not* a framework. egui is a library you call into, not an environment you program for. @@ -87,7 +87,7 @@ egui is *not* a framework. egui is a library you call into, not an environment y ### Non-goals -* Become the most powerful GUI libary +* Become the most powerful GUI library * Native looking interface * Advanced and flexible layouts (that's fundamentally incompatible with immediate mode) @@ -134,7 +134,11 @@ egui is in active development. It works well for what it does, but it lacks many * Tooltips on hover * More - + + +Light Theme: + + ## How it works @@ -234,7 +238,7 @@ The short of it is this: immediate mode GUI libraries are easier to use, but les The main advantage of immediate mode is that the application code becomes vastly simpler: * You never need to have any on-click handlers and callbacks that disrupts your code flow. -* You don't have to worry about a linger callback calling something that is gone. +* You don't have to worry about a lingering callback calling something that is gone. * Your GUI code can easily live in a simple function (no need for an object just for the UI). * You don't have to worry about app state and GUI state being out-of-sync (i.e. the GUI showing something outdated), because the GUI isn't storing any state - it is showing the latest state *immediately*. @@ -275,6 +279,17 @@ Overall, ID handling is a rare inconvenience, and not a big disadvantage. Also see [GitHub Discussions](https://github.com/emilk/egui/discussions/categories/q-a). +### Can I use `egui` with non-latin characters? +Yes! But you need to install your own font (`.ttf` or `.otf`) using `Context::set_fonts`. + +### Can I customize the look of egui? +Yes! You can customize the colors, spacing and sizes of everything. By default egui comes with a dark and a light theme. + +### What about accessibility, such as screen readers? +There is experimental support for a screen reader. In [the web demo](https://emilk.github.io/egui/index.html) you can enable it in the "Backend" tab. + +Read more at . + ### What is the difference between egui and eframe? `egui` is a 2D user interface library for laying out and interacting with buttons, sliders, etc. diff --git a/egui/src/context.rs b/egui/src/context.rs index deeb16fba76..8b20492bd2c 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -415,7 +415,12 @@ impl Context { self.fonts().texture() } - /// Will become active at the start of the next frame. + /// Tell `egui` which fonts to use. + /// + /// The default `egui` fonts only support latin and cyrillic alphabets, + /// but you can call this to install additional fonts that support e.g. korean characters. + /// + /// The new fonts will become active at the start of the next frame. pub fn set_fonts(&self, font_definitions: FontDefinitions) { if let Some(current_fonts) = &self.fonts { // NOTE: this comparison is expensive since it checks TTF data for equality @@ -434,6 +439,8 @@ impl Context { /// The [`Style`] used by all new windows, panels etc. /// + /// You can also use [`Ui::style_mut`] to change the style of a single [`Ui`]. + /// /// Example: /// ``` /// # let mut ctx = egui::CtxRef::default(); diff --git a/egui/src/data/input.rs b/egui/src/data/input.rs index f838ed1a853..172dde08feb 100644 --- a/egui/src/data/input.rs +++ b/egui/src/data/input.rs @@ -6,6 +6,9 @@ use crate::emath::*; /// /// Set the values that make sense, leave the rest at their `Default::default()`. /// +/// You can check if `egui` is using the inputs using +/// [`crate::Context::wants_pointer_input`] and [`crate::Context::wants_keyboard_input`]. +/// /// All coordinates are in points (logical pixels) with origin (0, 0) in the top left corner. #[derive(Clone, Debug)] pub struct RawInput { diff --git a/egui/src/input_state.rs b/egui/src/input_state.rs index d8162716080..d727f39acfc 100644 --- a/egui/src/input_state.rs +++ b/egui/src/input_state.rs @@ -14,6 +14,9 @@ const MAX_CLICK_DIST: f32 = 6.0; // TODO: move to settings const MAX_CLICK_DELAY: f64 = 0.3; // TODO: move to settings /// Input state that egui updates each frame. +/// +/// You can check if `egui` is using the inputs using +/// [`crate::Context::wants_pointer_input`] and [`crate::Context::wants_keyboard_input`]. #[derive(Clone, Debug)] pub struct InputState { /// The raw input we got this frame from the backend. diff --git a/egui/src/style.rs b/egui/src/style.rs index 01f96d920c2..e6445c9850f 100644 --- a/egui/src/style.rs +++ b/egui/src/style.rs @@ -5,7 +5,12 @@ use crate::{color::*, emath::*, Response}; use epaint::{Shadow, Stroke, TextStyle}; -/// Specifies the look and feel of a [`Ui`]. +/// Specifies the look and feel of egui. +/// +/// You can change the visuals of a [`Ui`] with [`Ui::style_mut`] +/// and of everything with [`crate::Context::set_style`]. +/// +/// If you want to change fonts, use [`crate::Context::set_fonts`] instead. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] @@ -22,11 +27,16 @@ pub struct Style { /// * `Some(false)`: default off pub wrap: Option, + /// Sizes and distances between widgets pub spacing: Spacing, + + /// How and when interaction happens. pub interaction: Interaction, + + /// Colors etc. pub visuals: Visuals, - /// How many seconds a typical animation should last + /// How many seconds a typical animation should last. pub animation_time: f32, /// Options to help debug why egui behaves strangely. @@ -58,6 +68,7 @@ impl Style { } } +/// Controls the sizes and distances between widgets. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] @@ -119,6 +130,7 @@ impl Spacing { } } +/// How and when interaction happens. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] @@ -133,6 +145,12 @@ pub struct Interaction { pub show_tooltips_only_when_still: bool, } +/// Controls the visual style (colors etc) of egui. +/// +/// You can change the visuals of a [`Ui`] with [`Ui::visuals_mut`] +/// and of everything with [`crate::Context::set_visuals`]. +/// +/// If you want to change fonts, use [`crate::Context::set_fonts`] instead. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] @@ -224,6 +242,7 @@ pub struct Selection { pub stroke: Stroke, } +/// The visuals of widgets for different states of interaction. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", serde(default))] diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 392022663d7..dde3d51193f 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -769,7 +769,26 @@ impl Ui { InnerResponse::new(ret, response) } - /// Convenience function to get a region to paint on + /// Convenience function to get a region to paint on. + /// + /// Note that egui uses screen coordinates for everything. + /// + /// ``` + /// # use egui::*; + /// # let mut ui = &mut egui::Ui::__test(); + /// # use std::f32::consts::TAU; + /// let size = Vec2::splat(16.0); + /// let (response, painter) = ui.allocate_painter(size, Sense::hover()); + /// let rect = response.rect; + /// let c = rect.center(); + /// let r = rect.width() / 2.0 - 1.0; + /// let color = Color32::from_gray(128); + /// let stroke = Stroke::new(1.0, color); + /// painter.circle_stroke(c, r, stroke); + /// painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], stroke); + /// painter.line_segment([c, c + r * Vec2::angled(TAU * 1.0 / 8.0)], stroke); + /// painter.line_segment([c, c + r * Vec2::angled(TAU * 3.0 / 8.0)], stroke); + /// ``` pub fn allocate_painter(&mut self, desired_size: Vec2, sense: Sense) -> (Response, Painter) { let response = self.allocate_response(desired_size, sense); let clip_rect = self.clip_rect().intersect(response.rect); // Make sure we don't paint out of bounds diff --git a/egui_demo_lib/src/apps/demo/font_book.rs b/egui_demo_lib/src/apps/demo/font_book.rs index 35f9290d301..b4b47f86880 100644 --- a/egui_demo_lib/src/apps/demo/font_book.rs +++ b/egui_demo_lib/src/apps/demo/font_book.rs @@ -58,11 +58,24 @@ impl super::View for FontBook { use super::font_contents_ubuntu::UBUNTU_FONT_CHARACTERS; ui.label(format!( - "egui supports {} standard characters and {} emojis.\nClick on a character to copy it.", + "The default egui fonts supports {} standard characters and {} emojis.", UBUNTU_FONT_CHARACTERS.len(), FULL_EMOJI_LIST.len(), )); + ui.horizontal_wrapped(|ui| { + ui.spacing_mut().item_spacing.x = 0.0; + ui.label("You can add more characters by installing additional fonts with "); + ui.add( + egui::Hyperlink::from_label_and_url( + "Context::set_fonts", + "https://docs.rs/egui/latest/egui/struct.Context.html#method.set_fonts", + ) + .text_style(egui::TextStyle::Monospace), + ); + ui.label("."); + }); + ui.separator(); egui::ComboBox::from_label("Text style") diff --git a/egui_demo_lib/src/apps/demo/misc_demo_window.rs b/egui_demo_lib/src/apps/demo/misc_demo_window.rs index 8a7d2a8b2e9..da463a0acd1 100644 --- a/egui_demo_lib/src/apps/demo/misc_demo_window.rs +++ b/egui_demo_lib/src/apps/demo/misc_demo_window.rs @@ -88,8 +88,9 @@ impl View for MiscDemoWindow { ui.horizontal(|ui| { ui.label("You can pretty easily paint your own small icons:"); use std::f32::consts::TAU; - let (rect, _response) = ui.allocate_at_least(Vec2::splat(16.0), Sense::hover()); - let painter = ui.painter(); + let size = Vec2::splat(16.0); + let (response, painter) = ui.allocate_painter(size, Sense::hover()); + let rect = response.rect; let c = rect.center(); let r = rect.width() / 2.0 - 1.0; let color = Color32::from_gray(128); diff --git a/epaint/src/text/fonts.rs b/epaint/src/text/fonts.rs index f9eb65f42b6..154d100b5f6 100644 --- a/epaint/src/text/fonts.rs +++ b/epaint/src/text/fonts.rs @@ -74,11 +74,42 @@ fn rusttype_font_from_font_data(name: &str, data: &FontData) -> rusttype::Font<' /// Often you would start with [`FontDefinitions::default()`] and then add/change the contents. /// /// ``` -/// let mut fonts = epaint::text::FontDefinitions::default(); +/// # use {epaint::text::{FontDefinitions, TextStyle, FontFamily}}; +/// # struct FakeEguiCtx {}; +/// # impl FakeEguiCtx { fn set_fonts(&self, _: FontDefinitions) {} } +/// # let ctx = FakeEguiCtx {}; +/// let mut fonts = FontDefinitions::default(); +/// /// // Large button text: /// fonts.family_and_size.insert( -/// epaint::text::TextStyle::Button, -/// (epaint::text::FontFamily::Proportional, 32.0)); +/// TextStyle::Button, +/// (FontFamily::Proportional, 32.0) +/// ); +/// +/// ctx.set_fonts(fonts); +/// ``` +/// +/// You can also install your own custom fonts: +/// ``` +/// # use {epaint::text::{FontDefinitions, TextStyle, FontFamily}}; +/// # struct FakeEguiCtx {}; +/// # impl FakeEguiCtx { fn set_fonts(&self, _: FontDefinitions) {} } +/// # let ctx = FakeEguiCtx {}; +/// let mut fonts = FontDefinitions::default(); +/// +/// // Install my own font (maybe supporting non-latin characters): +/// fonts.font_data.insert("my_font".to_owned(), +/// std::borrow::Cow::Borrowed(include_bytes!("../../fonts/Ubuntu-Light.ttf"))); // .ttf and .otf supported +/// +/// // Put my font first (highest priority): +/// fonts.fonts_for_family.get_mut(&FontFamily::Proportional).unwrap() +/// .insert(0, "my_font".to_owned()); +/// +/// // Put my font as last fallback for monospace: +/// fonts.fonts_for_family.get_mut(&FontFamily::Monospace).unwrap() +/// .push("my_font".to_owned()); +/// +/// ctx.set_fonts(fonts); /// ``` #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] diff --git a/media/light_theme.png b/media/light_theme.png new file mode 100644 index 00000000000..23927154375 Binary files /dev/null and b/media/light_theme.png differ diff --git a/media/widget_gallery.gif b/media/widget_gallery.gif new file mode 100644 index 00000000000..87ab9b54e5f Binary files /dev/null and b/media/widget_gallery.gif differ