egui icon indicating copy to clipboard operation
egui copied to clipboard

Add `ui.align_cursor()` to for the cursor to be aligned with physical pixels

Open abey79 opened this issue 1 year ago • 5 comments

Unaligned cursor lead to various visual glitches. It would be nice to have an API to "fix" the cursor position. This would avoid using code like this:

ui.add_space(-ui.cursor().min.y.fract());

which is not robust to layout orientation and assumes 1:1 physical pixels.

Related:

  • https://github.com/emilk/egui/issues/4927

abey79 avatar Aug 06 '24 12:08 abey79

There are three alternatives:

  • Round widgets to physical pixels
  • Round widgets to logical ui points
  • Only round when pixels_per_point is an integer
  • No rounding

I'd like to understand a bit more what the trade-offs are between these.

Regardless of how/if we round widgets during layout, we still want to round some visual shapes to physical pixels to make them crisper (text, thin lines, etc).

Rounding to physical pixels

Since we need to round some visual shapes to physical pixels anyway, always rounding to physical pixels may produce more even results.

Rounding to logical ui points

All calculations are done in logical ui points, and so if we make them integers we prevent rounding errors.


Unaligned cursor lead to various visual glitches.

I'd like a repro for this

emilk avatar Sep 16 '24 17:09 emilk

I'd like a repro for this

use eframe::egui;

fn main() -> eframe::Result {
    env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
        ..Default::default()
    };
    eframe::run_native(
        "My egui App",
        options,
        Box::new(|cc| {
            // This gives us image support:
            egui_extras::install_image_loaders(&cc.egui_ctx);

            Ok(Box::new(MyApp))
        }),
    )
}

struct MyApp;

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            // unaligned cursor
            ui.add_space(0.1234);

            ui.spacing_mut().item_spacing.y = 0.0;

            for _ in 0..5 {
                let (rect, _) =
                    ui.allocate_exact_size(egui::vec2(100.0, 20.0), egui::Sense::hover());
                ui.painter()
                    .rect_filled(rect, 0.0, ui.style().visuals.selection.bg_fill);
            }
        });
    }
}
image



Commenting out the mis-alignment line gives the expect result:

image



One notch of zooming-in (cmd-+) brings back the glitch though:

image

abey79 avatar Sep 17 '24 08:09 abey79

There are three alternatives:

  • Round widgets to physical pixels
  • Round widgets to logical ui points
  • Only round when pixels_per_point is an integer
  • No rounding

In light of the repro above, I believe rounding to physical pixels is what's needed, but, ultimately, whatever fixes this 😅

abey79 avatar Sep 17 '24 08:09 abey79

The problem there is the feathering (anti-aliasing) of the boxes.

The easy fix there would be to always align boxes to the pixel grid, and turn of feathering for them. The usefulness of feathering on axis-aligned boxes is minimal anyway. If you're animating boxes then it will make them move smoother, but that seems quite niche.

Related:

  • https://github.com/emilk/egui/pull/4943

emilk avatar Sep 17 '24 09:09 emilk

I think the correct thing to do here is to align ui points to integers in order to avoid rounding errors in width calculations and similar (see e.g. https://github.com/emilk/egui/issues/5084).

The problem of rendering rectangles side-by-side without a seem is a purely visual one, and can be solved separately by one of these means:

  • Tell the user to round rectangles to pixel grid before painting them, if they plan on putting two of them side-by-side
  • Always round filled rectangles to the pixel grid
  • Add a rounding mode option to RectShape

I've opened two new issues for this:

  • https://github.com/emilk/egui/issues/5163
  • https://github.com/emilk/egui/issues/5164

emilk avatar Sep 25 '24 08:09 emilk

Better solved by:

  • https://github.com/emilk/egui/issues/5163
  • https://github.com/emilk/egui/issues/5164

emilk avatar Dec 11 '24 15:12 emilk