Improve docs concerning custom fonts, themes and accessibility

Closes https://github.com/emilk/egui/pull/370
Closes https://github.com/emilk/egui/issues/372
This commit is contained in:
Emil Ernerfeldt 2021-05-11 14:56:27 +02:00
parent 8f8ba16696
commit 7b0f991b20
11 changed files with 127 additions and 16 deletions

View file

@ -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 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. 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 ## Goals
* The easiest to use GUI libary * The easiest to use GUI library
* Responsive: target 60 Hz in debug build * Responsive: target 60 Hz in debug build
* Friendly: difficult to make mistakes, and shouldn't panic * Friendly: difficult to make mistakes, and shouldn't panic
* Portable: the same code works on the web and as a native app * 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) * 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 * 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 * 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. 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 ### Non-goals
* Become the most powerful GUI libary * Become the most powerful GUI library
* Native looking interface * Native looking interface
* Advanced and flexible layouts (that's fundamentally incompatible with immediate mode) * 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 * Tooltips on hover
* More * More
<img src="media/widget_gallery_0.8.0.gif" width="50%"> <img src="media/widget_gallery.gif" width="50%">
Light Theme:
<img src="media/light_theme.png" width="50%">
## How it works ## 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: 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 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). * 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*. * 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). 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 <https://github.com/emilk/egui/issues/167>.
### What is the difference between egui and eframe? ### What is the difference between egui and eframe?
`egui` is a 2D user interface library for laying out and interacting with buttons, sliders, etc. `egui` is a 2D user interface library for laying out and interacting with buttons, sliders, etc.

View file

@ -415,7 +415,12 @@ impl Context {
self.fonts().texture() 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) { pub fn set_fonts(&self, font_definitions: FontDefinitions) {
if let Some(current_fonts) = &self.fonts { if let Some(current_fonts) = &self.fonts {
// NOTE: this comparison is expensive since it checks TTF data for equality // 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. /// 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: /// Example:
/// ``` /// ```
/// # let mut ctx = egui::CtxRef::default(); /// # let mut ctx = egui::CtxRef::default();

View file

@ -6,6 +6,9 @@ use crate::emath::*;
/// ///
/// Set the values that make sense, leave the rest at their `Default::default()`. /// 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. /// All coordinates are in points (logical pixels) with origin (0, 0) in the top left corner.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RawInput { pub struct RawInput {

View file

@ -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 const MAX_CLICK_DELAY: f64 = 0.3; // TODO: move to settings
/// Input state that egui updates each frame. /// 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)] #[derive(Clone, Debug)]
pub struct InputState { pub struct InputState {
/// The raw input we got this frame from the backend. /// The raw input we got this frame from the backend.

View file

@ -5,7 +5,12 @@
use crate::{color::*, emath::*, Response}; use crate::{color::*, emath::*, Response};
use epaint::{Shadow, Stroke, TextStyle}; 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)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
@ -22,11 +27,16 @@ pub struct Style {
/// * `Some(false)`: default off /// * `Some(false)`: default off
pub wrap: Option<bool>, pub wrap: Option<bool>,
/// Sizes and distances between widgets
pub spacing: Spacing, pub spacing: Spacing,
/// How and when interaction happens.
pub interaction: Interaction, pub interaction: Interaction,
/// Colors etc.
pub visuals: Visuals, pub visuals: Visuals,
/// How many seconds a typical animation should last /// How many seconds a typical animation should last.
pub animation_time: f32, pub animation_time: f32,
/// Options to help debug why egui behaves strangely. /// 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)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
@ -119,6 +130,7 @@ impl Spacing {
} }
} }
/// How and when interaction happens.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
@ -133,6 +145,12 @@ pub struct Interaction {
pub show_tooltips_only_when_still: bool, 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)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]
@ -224,6 +242,7 @@ pub struct Selection {
pub stroke: Stroke, pub stroke: Stroke,
} }
/// The visuals of widgets for different states of interaction.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))] #[cfg_attr(feature = "persistence", serde(default))]

View file

@ -769,7 +769,26 @@ impl Ui {
InnerResponse::new(ret, response) 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) { pub fn allocate_painter(&mut self, desired_size: Vec2, sense: Sense) -> (Response, Painter) {
let response = self.allocate_response(desired_size, sense); 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 let clip_rect = self.clip_rect().intersect(response.rect); // Make sure we don't paint out of bounds

View file

@ -58,11 +58,24 @@ impl super::View for FontBook {
use super::font_contents_ubuntu::UBUNTU_FONT_CHARACTERS; use super::font_contents_ubuntu::UBUNTU_FONT_CHARACTERS;
ui.label(format!( 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(), UBUNTU_FONT_CHARACTERS.len(),
FULL_EMOJI_LIST.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(); ui.separator();
egui::ComboBox::from_label("Text style") egui::ComboBox::from_label("Text style")

View file

@ -88,8 +88,9 @@ impl View for MiscDemoWindow {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label("You can pretty easily paint your own small icons:"); ui.label("You can pretty easily paint your own small icons:");
use std::f32::consts::TAU; use std::f32::consts::TAU;
let (rect, _response) = ui.allocate_at_least(Vec2::splat(16.0), Sense::hover()); let size = Vec2::splat(16.0);
let painter = ui.painter(); let (response, painter) = ui.allocate_painter(size, Sense::hover());
let rect = response.rect;
let c = rect.center(); let c = rect.center();
let r = rect.width() / 2.0 - 1.0; let r = rect.width() / 2.0 - 1.0;
let color = Color32::from_gray(128); let color = Color32::from_gray(128);

View file

@ -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. /// 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: /// // Large button text:
/// fonts.family_and_size.insert( /// fonts.family_and_size.insert(
/// epaint::text::TextStyle::Button, /// TextStyle::Button,
/// (epaint::text::FontFamily::Proportional, 32.0)); /// (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)] #[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]

BIN
media/light_theme.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
media/widget_gallery.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB