Giter Site home page Giter Site logo

emilk / egui Goto Github PK

View Code? Open in Web Editor NEW
19.2K 19.2K 1.4K 77.57 MB

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native

License: Apache License 2.0

Shell 0.41% Rust 98.54% Python 0.44% GLSL 0.06% WGSL 0.13% HTML 0.43%
game-development gamedev gui imgui rust wasm

egui's Introduction

πŸ–Œ egui: an easy-to-use GUI in pure Rust

github Latest version Documentation unsafe forbidden Build Status MIT Apache Discord

egui development is sponsored by Rerun, a startup building
an SDK for visualizing streams of multimodal data.


πŸ‘‰ Click to run the web demo πŸ‘ˆ

egui (pronounced "e-gooey") 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.

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.

eframe is the official egui framework, which supports writing apps for Web, Linux, Mac, Windows, and Android.

Example

ui.heading("My egui Application");
ui.horizontal(|ui| {
    ui.label("Your name: ");
    ui.text_edit_singleline(&mut name);
});
ui.add(egui::Slider::new(&mut age, 0..=120).text("age"));
if ui.button("Increment").clicked() {
    age += 1;
}
ui.label(format!("Hello '{name}', age {age}"));
ui.image(egui::include_image!("ferris.png"));

Dark mode Β  Β  Light mode

Sections:

(egui ηš„δΈ­ζ–‡ηΏ»θ―‘ζ–‡ζ‘£ / chinese translation)

Quick start

There are simple examples in the examples/ folder. If you want to write a web app, then go to https://github.com/emilk/eframe_template/ and follow the instructions. The official docs are at https://docs.rs/egui. For inspiration and more examples, check out the the egui web demo and follow the links in it to its source code.

If you want to integrate egui into an existing engine, go to the Integrations section.

If you have questions, use GitHub Discussions. There is also an egui discord server. If you want to contribute to egui, please read the Contributing Guidelines.

Demo

Click to run egui web demo (works in any browser with Wasm and WebGL support). Uses eframe.

To test the demo app locally, run cargo run --release -p egui_demo_app.

The native backend is egui_glow (using glow) and should work out-of-the-box on Mac and Windows, but on Linux you need to first run:

sudo apt-get install -y libclang-dev libgtk-3-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev

On Fedora Rawhide you need to run:

dnf install clang clang-devel clang-tools-extra libxkbcommon-devel pkg-config openssl-devel libxcb-devel gtk3-devel atk fontconfig-devel

NOTE: This is just for the demo app - egui itself is completely platform agnostic!

Goals

  • 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
  • Easy to integrate into any environment
  • A simple 2D graphics API for custom painting (epaint).
  • No callbacks
  • Pure immediate mode
  • Extensible: easy to write your own widgets for egui
  • 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

egui is not a framework. egui is a library you call into, not an environment you program for.

NOTE: egui does not claim to have reached all these goals yet! egui is still work in progress.

Non-goals

  • Become the most powerful GUI library
  • Native looking interface
  • Advanced and flexible layouts (that's fundamentally incompatible with immediate mode)

State

egui is in active development. It works well for what it does, but it lacks many features and the interfaces are still in flux. New releases will have breaking changes.

Still, egui can be used to create professional looking applications, like the Rerun Viewer.

Features

  • Widgets: label, text button, hyperlink, checkbox, radio button, slider, draggable value, text editing, color picker, spinner
  • Images
  • Layouts: horizontal, vertical, columns, automatic wrapping
  • Text editing: multiline, copy/paste, undo, emoji supports
  • Windows: move, resize, name, minimize and close. Automatically sized and positioned.
  • Regions: resizing, vertical scrolling, collapsing headers (sections), panels
  • Rendering: Anti-aliased rendering of lines, circles, text and convex polygons.
  • Tooltips on hover
  • Accessibility via AccessKit
  • Label text selection
  • And more!

Light Theme:

Dependencies

egui has a minimal set of default dependencies:

Heavier dependencies are kept out of egui, even as opt-in. No code that isn't fully Wasm-friendly is part of egui.

To load images into egui you can use the official egui_extras crate.

eframe on the other hand has a lot of dependencies, including winit, image, graphics crates, clipboard crates, etc,

Who is egui for?

egui aims to be the best choice when you want a simple way to create a GUI, or you want to add a GUI to a game engine.

If you are not using Rust, egui is not for you. If you want a GUI that looks native, egui is not for you. If you want something that doesn't break when you upgrade it, egui isn't for you (yet).

But if you are writing something interactive in Rust that needs a simple GUI, egui may be for you.

Integrations

egui is built to be easy to integrate into any existing game engine or platform you are working on. egui itself doesn't know or care on what OS it is running or how to render things to the screen - that is the job of the egui integration.

An integration needs to do the following each frame:

  • Input: Gather input (mouse, touches, keyboard, screen size, etc) and give it to egui
  • Call into the application GUI code
  • Output: Handle egui output (cursor changes, paste, texture allocations, …)
  • Painting: Render the triangle mesh egui produces (see OpenGL example)

Official integrations

These are the official egui integrations:

  • eframe for compiling the same app to web/wasm and desktop/native. Uses egui-winit and egui_glow or egui-wgpu
  • egui_glow for rendering egui with glow on native and web, and for making native apps
  • egui-wgpu for wgpu (WebGPU API)
  • egui-winit for integrating with winit

3rd party integrations

Writing your own egui integration

Missing an integration for the thing you're working on? Create one, it's easy! See https://docs.rs/egui/latest/egui/#integrating-with-egui.

Why immediate mode

egui is an immediate mode GUI library, as opposed to a retained mode GUI library. The difference between retained mode and immediate mode is best illustrated with the example of a button: In a retained GUI you create a button, add it to some UI and install some on-click handler (callback). The button is retained in the UI, and to change the text on it you need to store some sort of reference to it. By contrast, in immediate mode you show the button and interact with it immediately, and you do so every frame (e.g. 60 times per second). This means there is no need for any on-click handler, nor to store any reference to it. In egui this looks like this: if ui.button("Save file").clicked() { save(file); }.

A more detailed description of immediate mode can be found in the egui docs.

There are advantages and disadvantages to both systems.

The short of it is this: immediate mode GUI libraries are easier to use, but less powerful.

Advantages of immediate mode

Usability

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 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.

In other words, a whole lot of code, complexity and bugs are gone, and you can focus your time on something more interesting than writing GUI code.

Disadvantages of immediate mode

Layout

The main disadvantage of immediate mode is it makes layout more difficult. Say you want to show a small dialog window in the center of the screen. To position the window correctly the GUI library must first know the size of it. To know the size of the window the GUI library must first layout the contents of the window. In retained mode this is easy: the GUI library does the window layout, positions the window, then checks for interaction ("was the OK button clicked?").

In immediate mode you run into a paradox: to know the size of the window, we must do the layout, but the layout code also checks for interaction ("was the OK button clicked?") and so it needs to know the window position before showing the window contents. This means we must decide where to show the window before we know its size!

This is a fundamental shortcoming of immediate mode GUIs, and any attempt to resolve it comes with its own downsides.

One workaround is to store the size and use it the next frame. This produces a frame-delay for the correct layout, producing occasional flickering the first frame something shows up. egui does this for some things such as windows and grid layouts.

You can also call the layout code twice (once to get the size, once to do the interaction), but that is not only more expensive, it's also complex to implement, and in some cases twice is not enough. egui never does this.

For "atomic" widgets (e.g. a button) egui knows the size before showing it, so centering buttons, labels etc is possible in egui without any special workarounds.

CPU usage

Since an immediate mode GUI does a full layout each frame, the layout code needs to be quick. If you have a very complex GUI this can tax the CPU. In particular, having a very large UI in a scroll area (with very long scrollback) can be slow, as the content needs to be laid out each frame.

If you design the GUI with this in mind and refrain from huge scroll areas (or only lay out the part that is in view) then the performance hit is generally pretty small. For most cases you can expect egui to take up 1-2 ms per frame, but egui still has a lot of room for optimization (it's not something I've focused on yet). egui only repaints when there is interaction (e.g. mouse movement) or an animation, so if your app is idle, no CPU is wasted.

If your GUI is highly interactive, then immediate mode may actually be more performant compared to retained mode. Go to any web page and resize the browser window, and you'll notice that the browser is very slow to do the layout and eats a lot of CPU doing it. Resize a window in egui by contrast, and you'll get smooth 60 FPS at no extra CPU cost.

IDs

There are some GUI state that you want the GUI library to retain, even in an immediate mode library such as egui. This includes position and sizes of windows and how far the user has scrolled in some UI. In these cases you need to provide egui with a seed of a unique identifier (unique within the parent UI). For instance: by default egui uses the window titles as unique IDs to store window positions. If you want two windows with the same name (or one window with a dynamic name) you must provide some other ID source to egui (some unique integer or string).

egui also needs to track which widget is being interacted with (e.g. which slider is being dragged). egui uses unique IDs for this as well, but in this case the IDs are automatically generated, so there is no need for the user to worry about it. In particular, having two buttons with the same name is no problem (this is in contrast with Dear ImGui).

Overall, ID handling is a rare inconvenience, and not a big disadvantage.

FAQ

Also see GitHub Discussions.

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, fonts and sizes of everything using Context::set_style.

This is not yet as powerful as say CSS, but this is going to improve.

Here is an example (from https://github.com/a-liashenko/TinyPomodoro):

How do I use egui with async?

If you call .await in your GUI code, the UI will freeze, which is very bad UX. Instead, keep the GUI thread non-blocking and communicate with any concurrent tasks (async tasks or other threads) with something like:

How to I create a file dialog?

The async version of rfd supports both native and Wasm. See example app here https://github.com/woelper/egui_pick_file which also has a demo available via gitub pages.

What about accessibility, such as screen readers?

egui includes optional support for AccessKit, which currently implements the native accessibility APIs on Windows and macOS. This feature is enabled by default in eframe. For platforms that AccessKit doesn't yet support, including web, there is an experimental built-in screen reader; in the web demo you can enable it in the "Backend" tab.

The original discussion of accessibility in egui is at #167. Now that AccessKit support is merged, providing a strong foundation for future accessibility work, please open new issues on specific accessibility problems.

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 has no idea if it is running on the web or natively, and does not know how to collect input or show things on screen. That is the job of the integration or backend.

It is common to use egui from a game engine (using e.g. bevy_egui), but you can also use egui stand-alone using eframe. eframe has integration for web and native, and handles input and rendering. The frame in eframe stands both for the frame in which your egui app resides and also for "framework" (eframe is a framework, egui is a library).

How do I render 3D stuff in an egui area?

There are multiple ways to combine egui with 3D. The simplest way is to use a 3D library and have egui sit on top of the 3D view. See for instance bevy_egui or three-d.

If you want to embed 3D into an egui view there are two options:

Shape::Callback

Example:

Shape::Callback will call your code when egui gets painted, to show anything using whatever the background rendering context is. When using eframe this will be glow. Other integrations will give you other rendering contexts, if they support Shape::Callback at all.

Render-to-texture

You can also render your 3D scene to a texture and display it using ui.image(…). You first need to convert the native texture to an egui::TextureId, and how to do this depends on the integration you use.

Examples:

Other

Conventions and design choices

All coordinates are in screen space coordinates, with (0, 0) in the top left corner

All coordinates are in logical "points" which may consist of many physical pixels.

All colors have premultiplied alpha, unless otherwise stated.

egui uses the builder pattern for construction widgets. For instance: ui.add(Label::new("Hello").text_color(RED)); I am not a big fan of the builder pattern (it is quite verbose both in implementation and in use) but until Rust has named, default arguments it is the best we can do. To alleviate some of the verbosity there are common-case helper functions, like ui.label("Hello");.

Instead of using matching begin/end style function calls (which can be error prone) egui prefers to use FnOnce closures passed to a wrapping function. Lambdas are a bit ugly though, so I'd like to find a nicer solution to this. More discussion of this at #1004 (comment).

egui uses a single RwLock for short-time locks on each access of Context data. This is to leave implementation simple and transactional and allow users to run their UI logic in parallel. Instead of creating mutex guards, egui uses closures passed to a wrapping function, e.g. ctx.input(|i| i.key_down(Key::A)). This is to make it less likely that a user would accidentally double-lock the Context, which would lead to a deadlock.

Inspiration

The one and only Dear ImGui is a great Immediate Mode GUI for C++ which works with many backends. That library revolutionized how I think about GUI code and turned GUI programming from something I hated to do to something I now enjoy.

Name

The name of the library and the project is "egui" and pronounced as "e-gooey". Please don't write it as "EGUI".

The library was originally called "Emigui", but was renamed to "egui" in 2020.

Credits

egui author and maintainer: Emil Ernerfeldt (@emilk).

Notable contributions by:

egui is licensed under MIT OR Apache-2.0.

  • The flattening algorithm for the cubic bezier curve and quadratic bezier curve is from lyon_geom

Default fonts:


egui development is sponsored by Rerun, a startup building
an SDK for visualizing streams of multimodal data.

egui's People

Contributors

4jx avatar abey79 avatar barugon avatar c-git avatar coderedart avatar dusterthefirst avatar embersarc avatar emilk avatar follower avatar itsethra avatar ivan-mk7 avatar jprochazk avatar juancampa avatar kentathebugmaker avatar lampsitter avatar lictex avatar logandark avatar lunixbochs avatar mankinskin avatar mwcampbell avatar optozorax avatar parasyte avatar rustbasic avatar s-nie avatar teh-cmc avatar tosti007 avatar varphone avatar wumpf avatar ygorsouza avatar zoxc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

egui's Issues

Sliders width doesn't seem to include the input box

I am trying to add a slider and I just noticed they have an input box where the value is displayed.
However, this doesn't seem to always show.

Using this code it is visible:

craft_to_columns[1].add(
    egui::Slider::u32(&mut workbench_data.craft_to_count, 1..=20)
        .text("Craft to"),
    );

And it appears:
image

Setting the text to "" makes it dissapear:
image

It's also worth noting that the first example, the "Craft to" doesn't expand the column width either (you can see just the C).

Are egui and SDL2 compatible?

I'd like to integrate egui with my app (currently in development and in the process of picking a UI lib). It uses SDL2 for canvas, events, windows, etc.

Do you know of any issues running those two together (on Win, macOS, Linux)?

Invalid clip rect for the multiline text widget

The left edge of the multiline text widget is always invisible if I enable the glScissor with a clip rect from the egui.
clip_rect.min.x is always +2 from the valid value. The valid value is calculated from vertices for the rendering of a job.

image

My code to rendering the UI is very similar to egui_glium's painter.

It is really a bug or I am stupid?

OS: Win10 x64
Rust: 1.49

Image Widget does not sense clicks.

Hello,

It appears that the Image widget does not have a "sense" hooked up and therefore doesn't listen for button clicks.

At least, that's how it appeared after quickly comparing Image to Button implementations.

I'm not sure if this is intentional and I should wrap the image in some other container that detects clicks... or if this is indeed a bug / missing functionality.

What I'm trying to do is implement a simple Tile picker/chooser, and so I was looking to just show the image, detect clicks on it, get the mouse cursor offset into the widget, do the math, and pick the tile. I didn't see any other obvious way of doing it with the current EGUI types, so I went this route.

Thanks!

Other DragValue widgets are responding at the same time

Hello,

I'm trying to display a sort of table/spreadsheet with a label and a dragvalue next to it per row, by doing this:

ui.columns(2, |cols| {
    cols[0].label("var_a: ");
    cols[1].add(egui::DragValue::f32(&mut var_a));
});

ui.columns(2, |cols| {
    cols[0].label("var_b: ");
    cols[1].add(egui::DragValue::f32(&mut var_b));
});

The problem is that when I drag the var_a DragValue, the var_b DragValue also simultaneously responds, and both change at the same time. Am I doing something wrong?

Thanks!

Make `raw` field of `GliumInputState` struct public.

I am trying to write my own main loop for egui_glium integration for a project that required more control over it.

I hit a snag, the input_to_egui function takes the reference to the GliumInputState struct (which itself is public) but since I cannot access its private raw field I cannot use it for its intended purpose outside of the egui_glium crate.

For reference, I am writing a front end for my Chip-8 interpreter, and it requires access to the keyboard events (which I think cannot be fetched through egui or the integration context) and other events for accurate timing.

Move keyboard focus with tab to any interactive widget

It would be great to have support for keyboard only navigation (i.e a concept of focused widget and the ability to move focus to another one) that would allow "tab" key for example to move to the next widget and "return" would act as press, etc.

This in return would make it quite easy to add gamepad support as it would just behave as a subset of a regular keyboard.

Windows with hidden titles

First of all, awesome library so thanks for your work on this!

It would be cool if it supported hiding the window title. My use-case would be to display some panels like the one below:
image

The Stats text takes up a lot of space and would be better to hide it for this use-case.

Allow tabs for indentation in text edit widget

Hi,

it seems like the Tab key is not captured in the text edit widget, could that be ?

Like, when I put some debug output in the event handling loop, it seems like there's no event emitted at all ... just the outer frame flickers ...

Is there a way to capture the tab key ?

Best,
N

Making a scrollable combo box doesn't work as expected.

egui issue

I'm trying to list all the files in the combo box (as buttons). This will over spill the window area because the drop down is bigger than the window height as can be seen above. Since combo box itself doesn't seem to support scrolling out of the box, I thought of adding a ScrollArea within the combo box (since it's a container presumably).

This kind of works, in that I can scroll by moving the mouse up and down, but couple of things are broken:

  1. The scroll area width is the size of the Window width which is the parent container ui. I'm not sure how I can constrain it to the combo box dimensions.
  2. The vertical scroll bar doesn't appear (perhaps because it falls outside the window width).

Frankly, I expected the combo box to automatically take care of this. That is, it has a scroll area built into it, so that if the item dimensions > default combo box dimension, the comb box shows appropriate scroll bars (vertical/horizontal).

Failing the above, what would be the idiomatic way to do this in egui? I've included the source code for how I'm doing things now:

&egui::Window::new("Chipper")
            .fixed_pos(Pos2::new(0f32,0f32))
            //.default_size(vec2(WINDOW_WIDTH as f32, WINDOW_HEIGHT as f32))
            .resizable(false)
            .collapsible(false)
            .show(&mut egui_ctx, |ui| {
                if !is_paused {
                    ui.label(format!("FPS: {} ({} ms/frame)", fps, avg_frame_time));
                }
                else {
                    ui.label(format!("PAUSED"));
                }
                ui.add(Image::new(chip8_tex_id, vec2((CHIP8_DISPLAY_WIDTH * DISPLAY_SCALE) as f32, (CHIP8_DISPLAY_HEIGHT * DISPLAY_SCALE) as f32)));
                ui.label("");
                combo_box_with_label(ui, "ROM files", selected_rom, |ui| {
                    ScrollArea::from_max_height(50f32).show(ui, |ui| {
                        for (f, _p) in &rom_files {
                            if ui.button(f).clicked {
                                selected_rom = f;
                                chip8.boot_rom(rom_files.get(selected_rom).expect("No rom files to load!")).expect("Failed to load rom!");
                            };
                        }
                    })
                });
                //There is probably a better way to add line breaks in egui....
                ui.label("");
                if ui.checkbox(&mut use_vy_for_shift_operations, "Use Vy for shift operations").clicked {
                    chip8.shift_using_vy = use_vy_for_shift_operations;
                };  
                if ui.checkbox(&mut increment_i_on_ld_operations, "Increment I on  LD Vx operations").clicked {
                    chip8.increment_i_on_ld = increment_i_on_ld_operations;
                };
                ui.label("");
                ui.label("ESC = Pause/Resume.  F2 = Reset.");
                
        });

rom_files is a HashMap<String, String>.

I hope I'm not missing something blindingly obvious... :S

Vimium chrome plugin prevents text editing in web feature demo

Vimium if you aren't already familiar is a plug-in that listens for particular keypresses so that you can navigate links in your web browser just using the keyboard. Normally it plays well with web forms, only intercepting keypresses when a form isn't selected. However when I click inside the input box in the demo, all of my keypresses are still being captured by Vimium, so I can't actually type anything. It seems there is some "you are in an editing field" state that doesn't get communicated back to the browser. I'm not sure if you would consider fixing this in scope but I could see it being a barrier for using emigui for non-game web apps, where plugins may be relevant for accessibility. I use Vimium so I can navigate webpages with a finite speech recognition command set (only letters like "A", "F" etc need to be recognized to be able to verbally click around). My guess is the current state of web assembly API standardization would make fixing this a horror show but I thought I'd mention it in case it's not ;)

Disable widgets

There is currently no way to disable widgets (with few exceptions). One potential way to disable widgets would be like this:

ui.enabled(false, |ui| {
    ui.checkbox(...);
    ui.add(Slider::f32(...)));
});

where everything in the closure would be non-interactive (except perhaps for on_hover_text tooltips), and have a grayed out disabled look to them.

Values marked as `#[serde(skip)]` still overwritten when loading app state.

Hi,

I'm working on an app that has a callback field which should be ignored during saving/loading the app state:

#[derive(serde::Deserialize, serde::Serialize)]
pub struct MyApp {
    content: String,
    #[serde(skip)]
    callback: Option<Arc<Mutex<dyn FnMut(&String)>>>,
}

Nonetheless, when I configure the app with the callback before calling egui_glium::run(), the callback is overwritten when "load" is called.

This happened after the latest update ... before that, the app state was re-loaded manually before calling run(). Not sure if there's a good way to handle this ...

Exporting DemoEnvironment

Hi,

I'm currently implementing a wgpu based backend crate and a winit based platform crate and would like to use the DemoApp in my simple example.

Since I need to call the ui() function on the DemoApp, I need create a DemoEnvironment struct, which is marked pub but is not exported. Could you maybe export this struct?

Some widgets keep expanding due to non-integral pixels_per_point

Hi,

While testing the WGPU render backend and Winit platform crates I created, I get following odd error:

grafik

Some elments keep expanding once they are visible. In the screenshot you can see that the drawing area of the painting widget expands and keeps expanding. If you scroll down until the end of the drawing area is visible, the expanding will stop.

Anothe example:
grafik

Do you maybe know where I could start debugging this? I have a hard time figuring out how the backend or the windowing integration could cause this behavior.

The code for reproducing this problem can be found here can be found in this example project.

Demo for egui web too overwhelming

The demo for egui_web is very overwhelming when using egui/egui_web for the first time, the one used for the Glium one is much more readable and understand able for the first time someone would look at it.

Display needs to be magnifiable

Either ctl-scrollwheel or ctl-shift-+ needs to work to magnify the UI as a whole. This is pretty much a necessity both for accessibility (my vision is terrible) and for hi-DPI screens (I'm using a 4K monitor). If I understand correctly, the decision to work in screen coordinates may make this hard: however, it's a must-have for any application that is intended for widespread web deployment.

Nested scrolling behaves weirdly

OS: MacOS Catalina
Browser: Safari 13.1.3

When multiple scrollable elements are nested, I would expect a deepest one to scroll first, until it will be scrolled completely and then next one kicks in. In the demo, nested scroll views are scrolled simultaneously instead.

Screen Recording 2020-12-07 at 16 32 03 2020-12-07 16_40_06

winit backend

winit is a very popular windowing crate, it would be easier to jump into emigui if it existed.

How to get the current settings of a window

I cannot find a way to access the x, y, width and height of a window. I derived my Node struct from Window and I'd like to be able to put things inside relatively to these current parameters.

(sorry I'm a bit learning rust with this project, then it might be a dumb question)

[Feature Request] Manually control ScrollArea scroll offset

I have the currently scenario:

Peek.2020-12-21.00-46.mp4

that i want to control the scroll when moving the selected line keeping the focus at the active line.

This is what I got trying to achieve this:

Currently the offset state that controls the scroll position is not public. I tried creating an simple public fn to set the offset manually, but since we can't get the Id of the ScrollArea.source_id, we can't change the state. We only have access to the Id of the child_ui (Prepared.content_ui) inside the show() function.

Also since the Prepared state is passed from the begin() to the end(), we can't change it inside the add_contents(). We would need to get the state from the memory again in the end() if we changed it in the add_contents().

Maybe the way out is to pass the Id used to save the state in the return of the show: ScrollArea.show() -> ScrollResponse<R> similar to the CollapsingResponse.

There is probably better solution for this

Blurred text on 4k (3840*2160) screen in the miniquad demo

Hello,

I noticed that in the miniquad web demo https://not-fl3.github.io/miniquad-samples/emigui.html the text is blurred.
I tested it on both Firefox and Brave (chromium based) and in both cases the text is rendered in the same way.
Here is a screenshot with Firefox on the left and Brave on the right : https://imgur.com/LIdPU6d
The text become clear when I zoom out (at 67% zoom on brave the text is clear).

But the Egui feature demo https://emilk.github.io/emigui/index.html is rendering sharp text : https://imgur.com/pPRb14W

I noticed that imgur heavily rezises/compresses the screenshots I linked, so the blurriness of the text is not so clear but I can send them by email if you want (the text really is blurry).

I am on Linux 64bit (Manjaro) with a RTX 2080.

Thank you for your awesome work, I wish you the best of luck with this project πŸ‘

Load fonts dynamically

I'm looking for a way to load fonts dynamically, but I don't know how to implement it

Questions from a newbie

Some of these questions may be straightforward but I've never done in game ui or used imgui, and I'm working on my first rust project, so it's all new to me.

  • I've been reading through the example code try to get a handle on how things work. I see a lot of these sorts of show calls in the example: .show(ui, |ui| self.painting.ui(ui)); Why take the ui object and also a lambda taking a ui object? I started digging to try and see if the ui just gets passed in later but it looks like child ui's are getting passed in?

  • I guess more generally if the rendering isn't immediate mode, is each frame building one big call-list/vertex-buffer/etc from scratch, or is it somehow figuring out what has or hasn't changed between frames and only sending stuff to the gpu that's different?

  • Why is a bunch of the core app state serializable? For example the ExampleApp struct. I noticed state seems to be preserved across refreshes if you only manipulate the Examples window -- but if you open any other window than it resets the whole gui to its original state on refresh -- and there's an every frame call to save_memory, but it looks like ultimately that is is only going to save the Memory struct, so I'm not sure how that would interact with other application state.

Scrollbars behave as if clicked when moving the mouse outside the scrollbar handle

  1. Have a long scrollbar.
  2. Drag from the end of the scrollbar handle in a direction orthogonal to the scrollbar direction (so horizontal for a vertical scrollbar).
  3. Once the mouse moves even slightly outside the scrollbar handle the handle will snap so that the mouse is at the center of the scrollbar handle.

This leads to very jarring movements since while dragging a scrollbar you often move the mouse slightly outside the scrollbar handle. The issue also happens if you move the mouse quickly even in the same direction as the scrollbar.

Replace Glium

Glium is a convenient choice, but is a rather big dependency which compiles slowly, and uses build.rs. It is also no longer actively maintained.

The ideal choice for compiling Egui natively would be something simple that compiles quickly. I'm not sure there is such an alternative though.

One alternative worth investigating is pure glutin (which Glium depend on). It is a bit lower level compared to Glium, and requires use of unsafe, but it looks easy enough to use: https://gist.github.com/matthewjberger/9da00592b472b50ec1e6b3238719264b

Wrong texture is bound when rendering

Hi!

I'm trying to integrate egui into a wasm-bindgen app that also needs to draw some other geometry. Basically my app tries to do the job of your AppRunner class also using egui_web, but also rendering other things, and doing other things with the input events, etc.

For some reason all my widgets end up looking like this though:

demo

If I use something like SpectorJS I can debug the draw call and see that drawElements is called with the font atlas texture bound to sampler 0, which is why the lines look strange: I think it's drawing them with the texture atlas bound? It is also likely the same thing that is preventing the actual text from showing. I wonder if you know what could be wrong, of if there's anything I'm missing?

Here is how I first setup the egui state:

let mut backend = egui_web::WebBackend::new("mycanvas").expect("Failed to make a web backend for egui");
let mut web_input: WebInput = Default::default();

And here is how I draw every frame:

ctx.clear_color(0.1, 0.1, 0.2, 1.0);
ctx.clear(GL::COLOR_BUFFER_BIT | GL::DEPTH_BUFFER_BIT);

let raw_input = web_input.new_frame();
let ui = backend.begin_frame(raw_input);

let mut s = String::from("test");
let mut value = 0.0;
egui::Window::new("Debug").show(&ui.ctx(), |ui| {
    ui.label(format!("Hello, world {}", 123));
    if ui.button("Save").clicked {
        log::info!("Save!");
    }
    ui.text_edit(&mut s);
    ui.add(egui::Slider::f32(&mut value, 0.0..=1.0).text("float"));
});

let (output, paint_jobs) = backend.end_frame().unwrap();
backend.paint(paint_jobs).expect("Failed to paint!");

ctx.viewport(
    0,
    0,
    canvas_width_on_screen as i32,
    canvas_height_on_screen as i32,
);

cube.render(
    &context,
    (js_sys::Date::now() - start_millis) as f32,
    curr_state.canvas_width,
    curr_state.canvas_height,
);

This should be working right? Or is there some magical step that AppRunner does that I overlooked?

Thanks!

Cmd+arrow default action is not prevented

OS: MacOS Catalina
Browser: Safari 13.1.3

When focusing text input, cmd+arrow hotkey not only moves cursor to beginning/end of input (as expected), but also switches tab to prev/next, which is undesirable.

Widget Gallery

Hello and Happy upcoming New Year!

I am doing integration between EGUI and our game engine Dotrix. We don't have our UI crate yet and I am about to use EGUI for an editor for now. Results looks like this so far
Snímek obrazovky 2020-12-30 v 23 31 59

My question if there is already some widget gallery maybe as an image with widgets and their names? Or should I just refer this list https://docs.rs/egui/0.6.0/egui/widgets/index.html ?

API discrepancy

With imgui-rs, the API is pretty much the same for everything.
WidgetType::new().param(args).build(&ui, || closure executed if widget is open for example for windows or buttons)
(Actually, that's not entirely true, small widgets use ifs but their API is moving toward this version by version)

However while looking through egui's source code, I've seen 4 ways the API is declared.
menu does everything in the constructor:
menu::menu(ui, "Windows", |ui| { ... })

Button uses ifs and ui.add:
if ui.add(Button::new("Clear memory")).clicked { ... }

Windows looks a bit like imgui:
Window::new("Examples").show(ctx, |ui| { ... })

collapsing comes directly from the ui object:
ui.collapsing("About Egui", |ui| { ... })

4 different ways of doing control flow for the same idea, which makes it a bit awkward to use.

PS: I'm doing a lot of issues but it's because I'm seriously considering the crate for my project and there's a few quirks before I'm thinking of moving to it. I absolutely admire the amount of work you've put into it !

Firefox CPU usage skyrockets unlike non-WebGL web pages

I have been exploring the Immediate Mode GUI concept, and I found a few demos for web applications, including emilk/egui.

One thing I noticed is that the CPU usage by Firefox skyrockets when I play with those demos, unliked with any other pages written with using HTML/CSS/JavaScript.

Q1. Why it happens and is this expected?
Q2. What happens if most of the websites I open in 50+ tabs use the Immediate Mode GUI - will Firefox and OS struggle?

Any input appreciated.

Many thanks,
Anton

No lightweight way to adjust ui style

I could be wrong about the following. I'm just starting to learn Rust, so I could have misunderstood something. I believe this is the only way to change style elements given the current API:

let mut style = ui.style().clone();
style.item_spacing = egui::vec2(x, y);
ui.set_style(style);

The ui's style member is private and the ui.style() method returns an immutable reference. So the only way to change a single style element is to clone the whole struct out, make the change, and pass ownership back into the ui. This is awkward to implement, and forces needless copies. It's also redundant with the clone that happens when a child ui is created. Touching a given ui's style won't affect global style settings.

The patterns in the Style::ui widget hints that some kind of public access is safe and may have been intended, but I can't find a pathway for it.

Are egui contributions welcome?

... or it is still too early?

Nothing egui-related in the works yet but I am asking since I plan to work with your lib for my project.

Custom fonts

Egui currently ships with built-in fonts and (as far as I could see) there's no way for the library user to switch those out.

Font customisation is done via ctx.set_fonts which accepts a FontDefinitions. But that requires a FontFamily which is an enum containing the hardcoded fonts and a no way to change them.

Would bringing your own font be within Egui's scope? The built-in fonts are great for e.g. debugging, level editor etc. UIs, but for a user-facing one (e.g. in-game menu, windows) the fonts often need to match the look & feel of the game.

If this is welcome, I'll be happy to work on it (albeit slowly) and follow any design considerations you might have.

My initial thought is adding a third variant FontFamily::Custom(u32) and provide a way to register a new font e.g.: impl Context { pub add_custom_font(font: &[u8]) -> u8; }

Where the function and the enum variant would both use a font ID (or maybe a hashed name) to support multiple fonts at the same time.

For reference, this is a commit I'm maintaining in my Egui fork to be able to use a (single) custom font:

tomassedovic@47c7942

And it's used like this in my game:

let font_definitions = {
    use egui::paint::fonts::{FontFamily, TextStyle};
    let family = FontFamily::Mononoki;

    let mut def = egui::paint::FontDefinitions::default();
    def.fonts.insert(TextStyle::Body, (family, font_size_px));
    def.fonts.insert(TextStyle::Button, (family, font_size_px));
    def.fonts.insert(TextStyle::Heading, (family, font_size_px));
    def.fonts
        .insert(TextStyle::Monospace, (family, font_size_px));
    def
};
ctx.set_fonts(font_definitions);

Repaint signal cannot be shared between threads

Hey,

I've been having problems sharing the repaint signal between threads. It seems the problem is that Arc requires that it wraps types that implement send and sync. Though I am new enough to rust that it could also be my fault.

The following code (in the ui function) will cause the error.

let repaint_signal = integration_context.repaint_signal.clone();
let thread = std::thread::spawn(|| {
  repaint_signal.request_repaint();
});
thread.join();

I'm happy to fix this issue if the problem wasn't caused by my own lack of understanding.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.