diff --git a/TODO.md b/TODO.md index 134c2b15..923ac527 100644 --- a/TODO.md +++ b/TODO.md @@ -3,6 +3,10 @@ TODO-list for the Egui project. If you looking for something to do, look here. * Widgets + * [ ] Tooltips: + * [ ] Tooltip widget: Something that looks like this: (?) :that shows text on hover. + * [ ] ui.info_button().tooltip_text("More info here"); + * [ ] Allow adding multiple tooltips to the same widget, showing them all one after the other. * [ ] Text input * [x] Input * [x] Text focus @@ -37,16 +41,17 @@ TODO-list for the Egui project. If you looking for something to do, look here. * [ ] Get modifier keys * [ ] Keyboard shortcuts * [ ] Copy, paste, undo, ... -* [ ] Text +* Text * [/] Unicode * [x] Shared mutable expanding texture map * [ ] Text editing of unicode * [ ] Change text style/color and continue in same layout -* [ ] Menu bar (File, Edit, etc) +* Menu bar (File, Edit, etc) * [ ] Sub-menus * [ ] Keyboard shortcuts -* [ ] Layout +* Layout * [x] Generalize Layout (separate from Ui) + * [ ] Break out `Region` with min_size + max_size + cursor + layout * [ ] Table with resizable columns * [ ] Grid layout * [ ] Point list @@ -54,9 +59,6 @@ TODO-list for the Egui project. If you looking for something to do, look here. * [ ] Positioning preference: `window.preference(Top, Right)` * [ ] Keeping right/bottom on expand. Maybe cover jitteryness with quick animation? * [ ] Make auto-positioning of windows respect permanent side-bars. -* [x] Image support - * [x] Show user textures - * [x] API for creating a texture managed by `egui::app::Backend` * Visuals * [x] Pixel-perfect painting (round positions to nearest pixel). * [x] Fix `aa_size`: should be 1, currently fudged at 1.5 @@ -66,9 +68,7 @@ TODO-list for the Egui project. If you looking for something to do, look here. * [x] sRGBA decode in fragment shader * [ ] Fix alpha blending / sRGB weirdness in WebGL (EXT_sRGB) * [ ] Thin circles look bad - * [ ] Allow adding multiple tooltips to the same widget, showing them all one after the other. -* Math - * [x] Change `width.min(max_width)` to `width.at_most(max_width)` + * [ ] Optimize small filled circles with the global texture. * Id * struct TempId(u64); struct StateId(u64); * `TempId` is count-based. Only good for interaction. Can't be used for storing state. @@ -79,14 +79,32 @@ TODO-list for the Egui project. If you looking for something to do, look here. * Manual layout example: * ui.child_ui_pos(pos).label("Label at specific position"); * ui.child_ui_rect(rect).label("Label in a rectangle"); +* Reactive mode + * [ ] Ask Egui if an event requires repainting + * [ ] Only repaint when mouse is over a Egui window (or is pressed and there is an active widget) -## egui_web +## Backends + +* [ ] Extract egui_app as egui_backend + +* egui_glium +* egui_web + * [ ] async HTTP requests +* [ ] egui_bitmap: slow reference rasterizer for tests + * Port https://github.com/emilk/imgui_software_renderer + * Less important: fast rasterizer for embedded 🤷‍♀️ +* [ ] egui_terminal (think ncurses) + * [ ] replace `round_to_pixel` with `round_to_X` where user can select X to be e.g. width of a letter +* [ ] egui_svg: No idea what this would be for :) + +### egui_web * [x] Scroll input * [x] Change to resize cursor on hover * [x] Port most code to Rust * [x] Read url fragment and redirect to a subpage (e.g. different examples apps)] -* [ ] Fix WebGL colors/beldning (try EXT_sRGB) +* [ ] Async HTTP requests +* [ ] Fix WebGL colors/blending (try EXT_sRGB) * [ ] Embeddability * [ ] Support canvas that does NOT cover entire screen. * [ ] Support multiple eguis in one web page. @@ -101,7 +119,7 @@ TODO-list for the Egui project. If you looking for something to do, look here. * [ ] `trait Container` (`Frame`, `Resize`, `ScrollArea`, ...) * [ ] `widget::TextButton` implemented as a `container::Button` which contains a `widget::Label`. * [ ] Easily chain `Container`s without nested closures. - * e.g. `ui.containers((Frame::new(), Resize::new(), ScrollArea::new()), |ui| ...)` + * e.g. `ui.wrap(Frame::new()).wrap(Resize::new()).wrap(ScrollArea::new()).show(|ui| ...)` * [ ] Attach labels to checkboxes, radio buttons and sliders with a separate wrapper-widget ? ### Refactor space allocation @@ -112,6 +130,8 @@ When painting a widget, you want to allocate space. On that space you sometimes * `ui.canvas(size) -> Paint` * `ui.child_ui(size) -> Ui` +This is a good place to support whole-widget culling. If a swidget is not visible, the above functions should maybe return `None` so that the widget code can early-out. + ## Other * [x] Persist UI state in external storage @@ -120,6 +140,7 @@ When painting a widget, you want to allocate space. On that space you sometimes * [ ] Build in a profiler which tracks which `Ui` in which window takes up CPU. * [ ] Draw as flame graph * [ ] Draw as hotmap + * [ ] Integrate puffin? * [ ] Windows should open from `UI`s and be boxed by parent ui. * Then we could open the example app inside a window in the example app, recursively. * [ ] Implement a minimal markdown viewer @@ -157,3 +178,6 @@ Ability to do a search for any widget. The search works even for collapsed regio * [x] Use clip rectangles when interacting * [x] Adjust clip rects so edges of child widgets aren't clipped * [x] Use HW clip rects +* [x] Image support + * [x] Show user textures + * [x] API for creating a texture managed by `egui::app::Backend` diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index 08e4b9d8..f1dc6231 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -286,7 +286,6 @@ impl<'open> Window<'open> { let outer_rect = frame.end(&mut area_content_ui); if possible.resizable { - // TODO: draw BEHIND contents ? paint_resize_corner(&mut area_content_ui, outer_rect, frame_stroke); } diff --git a/egui/src/demos/app.rs b/egui/src/demos/app.rs index 31564f5a..e1131ec1 100644 --- a/egui/src/demos/app.rs +++ b/egui/src/demos/app.rs @@ -142,7 +142,7 @@ impl FrameHistory { for (time, cpu_usage) in history.iter() { let age = (right_side_time - time) as f32; - let x = remap(age, history.max_age()..=0.0, rect.range_x()); + let x = remap(age, history.max_age()..=0.0, rect.x_range()); let y = remap_clamp(cpu_usage, 0.0..=graph_top_cpu_usage, rect.bottom_up_range()); cmds.push(PaintCmd::line_segment( diff --git a/egui/src/demos/color_test.rs b/egui/src/demos/color_test.rs index c6ac33f4..054fdc6e 100644 --- a/egui/src/demos/color_test.rs +++ b/egui/src/demos/color_test.rs @@ -269,7 +269,7 @@ fn vertex_gradient(ui: &mut Ui, bg_fill: Srgba, gradient: &Gradient) -> Response let mut triangles = Triangles::default(); for (i, &color) in gradient.0.iter().enumerate() { let t = i as f32 / (n as f32 - 1.0); - let x = lerp(rect.range_x(), t); + let x = lerp(rect.x_range(), t); triangles.colored_vertex(pos2(x, rect.top()), color); triangles.colored_vertex(pos2(x, rect.bottom()), color); if i < n - 1 { diff --git a/egui/src/introspection.rs b/egui/src/introspection.rs index f9cf37e4..d3575a0d 100644 --- a/egui/src/introspection.rs +++ b/egui/src/introspection.rs @@ -32,8 +32,8 @@ impl Texture { show_tooltip(ui.ctx(), |ui| { let pos = ui.input().mouse.pos.unwrap_or_else(|| ui.left_top()); let zoom_rect = ui.allocate_space(vec2(128.0, 128.0)); - let u = remap_clamp(pos.x, rect.range_x(), 0.0..=tex_w); - let v = remap_clamp(pos.y, rect.range_y(), 0.0..=tex_h); + let u = remap_clamp(pos.x, rect.x_range(), 0.0..=tex_w); + let v = remap_clamp(pos.y, rect.y_range(), 0.0..=tex_h); let texel_radius = 32.0; let u = u.at_least(texel_radius).at_most(tex_w - texel_radius); diff --git a/egui/src/math/rect.rs b/egui/src/math/rect.rs index 0b43e709..d046e404 100644 --- a/egui/src/math/rect.rs +++ b/egui/src/math/rect.rs @@ -139,14 +139,12 @@ impl Rect { self.width() * self.height() } - pub fn range_x(&self) -> RangeInclusive { + pub fn x_range(&self) -> RangeInclusive { self.min.x..=self.max.x } - - pub fn range_y(&self) -> RangeInclusive { + pub fn y_range(&self) -> RangeInclusive { self.min.y..=self.max.y } - pub fn bottom_up_range(&self) -> RangeInclusive { self.max.y..=self.min.y } diff --git a/egui/src/ui.rs b/egui/src/ui.rs index 4069cd4b..1dbda0b2 100644 --- a/egui/src/ui.rs +++ b/egui/src/ui.rs @@ -711,8 +711,13 @@ impl Ui { } /// Start a ui with horizontal layout. - /// Elements will be centered on the Y axis. - /// Initial height is `style.spacing.interact_size.y`. + /// After you have called this, the registers the contents as any other widget. + /// + /// Elements will be centered on the Y axis, i.e. + /// adjusted up and down to lie in the center of the horizontal layout. + /// The initial height is `style.spacing.interact_size.y`. + /// Centering is almost always what you want if you are + /// planning to to mix widgets or just different types of text. pub fn horizontal(&mut self, add_contents: impl FnOnce(&mut Ui) -> R) -> (R, Rect) { let initial_size = vec2( self.available().width(), diff --git a/egui/src/widgets/mod.rs b/egui/src/widgets/mod.rs index 21cb1559..090ce6cf 100644 --- a/egui/src/widgets/mod.rs +++ b/egui/src/widgets/mod.rs @@ -367,6 +367,7 @@ impl<'a> Widget for Checkbox<'a> { let mut desired_size = button_padding + vec2(icon_width + icon_spacing, 0.0) + galley.size + button_padding; desired_size = desired_size.at_least(spacing.interact_size); + desired_size.y = desired_size.y.max(icon_width); let rect = ui.allocate_space(desired_size); let response = ui.interact(rect, id, Sense::click()); @@ -450,6 +451,7 @@ impl Widget for RadioButton { let mut desired_size = button_padding + vec2(icon_width + icon_spacing, 0.0) + galley.size + button_padding; desired_size = desired_size.at_least(ui.style().spacing.interact_size); + desired_size.y = desired_size.y.max(icon_width); let rect = ui.allocate_space(desired_size); let response = ui.interact(rect, id, Sense::click()); diff --git a/egui_glium/src/lib.rs b/egui_glium/src/lib.rs index 11d9feaf..30668aa3 100644 --- a/egui_glium/src/lib.rs +++ b/egui_glium/src/lib.rs @@ -63,8 +63,10 @@ pub fn input_to_egui( } KeyboardInput { input, .. } => { if let Some(virtual_keycode) = input.virtual_keycode { - // TODO: If mac - if input.modifiers.logo() && virtual_keycode == VirtualKeyCode::Q { + if cfg!(target_os = "macos") + && input.modifiers.logo() + && virtual_keycode == VirtualKeyCode::Q + { *control_flow = ControlFlow::Exit; } @@ -174,7 +176,7 @@ pub fn handle_output( ) { if let Some(url) = output.open_url { if let Err(err) = webbrowser::open(&url) { - eprintln!("Failed to open url: {}", err); // TODO show error in imgui + eprintln!("Failed to open url: {}", err); } }