Round widget coordinates to even multiple of 1/32 (#5517)
* Closes https://github.com/emilk/egui/pull/5197 * Closes https://github.com/emilk/egui/issues/5163 This should help prevent rounding errors in layout code. @lucasmerlin you may wanna test this with `egui_flex`
This commit is contained in:
parent
f30f5e9578
commit
dfcc679d5a
|
|
@ -2,6 +2,8 @@
|
|||
//! It has no frame or own size. It is potentially movable.
|
||||
//! It is the foundation for windows and popups.
|
||||
|
||||
use emath::GuiRounding as _;
|
||||
|
||||
use crate::{
|
||||
emath, pos2, Align2, Context, Id, InnerResponse, LayerId, NumExt, Order, Pos2, Rect, Response,
|
||||
Sense, Ui, UiBuilder, UiKind, UiStackInfo, Vec2, WidgetRect, WidgetWithState,
|
||||
|
|
@ -66,6 +68,7 @@ impl AreaState {
|
|||
pivot_pos.x - self.pivot.x().to_factor() * size.x,
|
||||
pivot_pos.y - self.pivot.y().to_factor() * size.y,
|
||||
)
|
||||
.round_ui()
|
||||
}
|
||||
|
||||
/// Move the left top positions of the area.
|
||||
|
|
@ -80,7 +83,7 @@ impl AreaState {
|
|||
/// Where the area is on screen.
|
||||
pub fn rect(&self) -> Rect {
|
||||
let size = self.size.unwrap_or_default();
|
||||
Rect::from_min_size(self.left_top_pos(), size)
|
||||
Rect::from_min_size(self.left_top_pos(), size).round_ui()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -493,12 +496,11 @@ impl Area {
|
|||
|
||||
if constrain {
|
||||
state.set_left_top_pos(
|
||||
ctx.constrain_window_rect_to_area(state.rect(), constrain_rect)
|
||||
.min,
|
||||
Context::constrain_window_rect_to_area(state.rect(), constrain_rect).min,
|
||||
);
|
||||
}
|
||||
|
||||
state.set_left_top_pos(ctx.round_pos_to_pixels(state.left_top_pos()));
|
||||
state.set_left_top_pos(state.left_top_pos());
|
||||
|
||||
// Update response with possibly moved/constrained rect:
|
||||
move_response.rect = state.rect();
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
//!
|
||||
//! Add your [`crate::Window`]:s after any top-level panels.
|
||||
|
||||
use emath::GuiRounding as _;
|
||||
|
||||
use crate::{
|
||||
lerp, vec2, Align, Context, CursorIcon, Frame, Id, InnerResponse, LayerId, Layout, NumExt,
|
||||
Rangef, Rect, Sense, Stroke, Ui, UiBuilder, UiKind, UiStackInfo, Vec2,
|
||||
|
|
@ -264,6 +266,8 @@ impl SidePanel {
|
|||
}
|
||||
}
|
||||
|
||||
panel_rect = panel_rect.round_ui();
|
||||
|
||||
let mut panel_ui = ui.new_child(
|
||||
UiBuilder::new()
|
||||
.id_salt(id)
|
||||
|
|
@ -756,6 +760,8 @@ impl TopBottomPanel {
|
|||
}
|
||||
}
|
||||
|
||||
panel_rect = panel_rect.round_ui();
|
||||
|
||||
let mut panel_ui = ui.new_child(
|
||||
UiBuilder::new()
|
||||
.id_salt(id)
|
||||
|
|
@ -1130,7 +1136,6 @@ impl CentralPanel {
|
|||
ctx: &Context,
|
||||
add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
|
||||
) -> InnerResponse<R> {
|
||||
let available_rect = ctx.available_rect();
|
||||
let id = Id::new((ctx.viewport_id(), "central_panel"));
|
||||
|
||||
let mut panel_ui = Ui::new(
|
||||
|
|
@ -1138,7 +1143,7 @@ impl CentralPanel {
|
|||
id,
|
||||
UiBuilder::new()
|
||||
.layer_id(LayerId::background())
|
||||
.max_rect(available_rect),
|
||||
.max_rect(ctx.available_rect().round_ui()),
|
||||
);
|
||||
panel_ui.set_clip_rect(ctx.screen_rect());
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,8 @@ impl Resize {
|
|||
.at_most(self.max_size)
|
||||
.at_most(
|
||||
ui.ctx().screen_rect().size() - ui.spacing().window_margin.sum(), // hack for windows
|
||||
);
|
||||
)
|
||||
.round_ui();
|
||||
|
||||
State {
|
||||
desired_size: default_size,
|
||||
|
|
@ -233,7 +234,8 @@ impl Resize {
|
|||
state.desired_size = state
|
||||
.desired_size
|
||||
.at_least(self.min_size)
|
||||
.at_most(self.max_size);
|
||||
.at_most(self.max_size)
|
||||
.round_ui();
|
||||
|
||||
let mut user_requested_size = state.requested_size.take();
|
||||
|
||||
|
|
@ -383,6 +385,7 @@ impl Resize {
|
|||
}
|
||||
}
|
||||
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::Stroke;
|
||||
|
||||
pub fn paint_resize_corner(ui: &Ui, response: &Response) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
Align, Align2, Context, CursorIcon, Id, InnerResponse, LayerId, NumExt, Order, Response, Sense,
|
||||
TextStyle, Ui, UiKind, Vec2b, WidgetInfo, WidgetRect, WidgetText, WidgetType,
|
||||
};
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::{emath, pos2, vec2, Galley, Pos2, Rect, RectShape, Rounding, Shape, Stroke, Vec2};
|
||||
|
||||
use super::scroll_area::ScrollBarVisibility;
|
||||
|
|
@ -788,13 +789,12 @@ fn resize_response(
|
|||
area: &mut area::Prepared,
|
||||
resize_id: Id,
|
||||
) {
|
||||
let Some(new_rect) = move_and_resize_window(ctx, &resize_interaction) else {
|
||||
let Some(mut new_rect) = move_and_resize_window(ctx, &resize_interaction) else {
|
||||
return;
|
||||
};
|
||||
let mut new_rect = ctx.round_rect_to_pixels(new_rect);
|
||||
|
||||
if area.constrain() {
|
||||
new_rect = ctx.constrain_window_rect_to_area(new_rect, area.constrain_rect());
|
||||
new_rect = Context::constrain_window_rect_to_area(new_rect, area.constrain_rect());
|
||||
}
|
||||
|
||||
// TODO(emilk): add this to a Window state instead as a command "move here next frame"
|
||||
|
|
@ -819,18 +819,18 @@ fn move_and_resize_window(ctx: &Context, interaction: &ResizeInteraction) -> Opt
|
|||
let mut rect = interaction.start_rect; // prevent drift
|
||||
|
||||
if interaction.left.drag {
|
||||
rect.min.x = ctx.round_to_pixel(pointer_pos.x);
|
||||
rect.min.x = pointer_pos.x;
|
||||
} else if interaction.right.drag {
|
||||
rect.max.x = ctx.round_to_pixel(pointer_pos.x);
|
||||
rect.max.x = pointer_pos.x;
|
||||
}
|
||||
|
||||
if interaction.top.drag {
|
||||
rect.min.y = ctx.round_to_pixel(pointer_pos.y);
|
||||
rect.min.y = pointer_pos.y;
|
||||
} else if interaction.bottom.drag {
|
||||
rect.max.y = ctx.round_to_pixel(pointer_pos.y);
|
||||
rect.max.y = pointer_pos.y;
|
||||
}
|
||||
|
||||
Some(rect)
|
||||
Some(rect.round_ui())
|
||||
}
|
||||
|
||||
fn resize_interaction(
|
||||
|
|
@ -1070,7 +1070,7 @@ impl TitleBar {
|
|||
let item_spacing = ui.spacing().item_spacing;
|
||||
let button_size = Vec2::splat(ui.spacing().icon_width);
|
||||
|
||||
let pad = (height - button_size.y) / 2.0; // calculated so that the icon is on the diagonal (if window padding is symmetrical)
|
||||
let pad = ((height - button_size.y) / 2.0).round_ui(); // calculated so that the icon is on the diagonal (if window padding is symmetrical)
|
||||
|
||||
if collapsible {
|
||||
ui.add_space(pad);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
use std::{borrow::Cow, cell::RefCell, panic::Location, sync::Arc, time::Duration};
|
||||
|
||||
use containers::area::AreaState;
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::{
|
||||
emath::{self, TSTransform},
|
||||
mutex::RwLock,
|
||||
|
|
@ -2121,7 +2122,7 @@ impl Context {
|
|||
// ---------------------------------------------------------------------
|
||||
|
||||
/// Constrain the position of a window/area so it fits within the provided boundary.
|
||||
pub(crate) fn constrain_window_rect_to_area(&self, window: Rect, area: Rect) -> Rect {
|
||||
pub(crate) fn constrain_window_rect_to_area(window: Rect, area: Rect) -> Rect {
|
||||
let mut pos = window.min;
|
||||
|
||||
// Constrain to screen, unless window is too large to fit:
|
||||
|
|
@ -2133,9 +2134,7 @@ impl Context {
|
|||
pos.y = pos.y.at_most(area.bottom() + margin_y - window.height()); // move right if needed
|
||||
pos.y = pos.y.at_least(area.top() - margin_y); // move down if needed
|
||||
|
||||
pos = self.round_pos_to_pixels(pos);
|
||||
|
||||
Rect::from_min_size(pos, window.size())
|
||||
Rect::from_min_size(pos, window.size()).round_ui()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2568,7 +2567,7 @@ impl Context {
|
|||
|
||||
/// Position and size of the egui area.
|
||||
pub fn screen_rect(&self) -> Rect {
|
||||
self.input(|i| i.screen_rect())
|
||||
self.input(|i| i.screen_rect()).round_ui()
|
||||
}
|
||||
|
||||
/// How much space is still available after panels has been added.
|
||||
|
|
@ -2576,7 +2575,7 @@ impl Context {
|
|||
/// This is the "background" area, what egui doesn't cover with panels (but may cover with windows).
|
||||
/// This is also the area to which windows are constrained.
|
||||
pub fn available_rect(&self) -> Rect {
|
||||
self.pass_state(|s| s.available_rect())
|
||||
self.pass_state(|s| s.available_rect()).round_ui()
|
||||
}
|
||||
|
||||
/// How much space is used by panels and windows.
|
||||
|
|
@ -2586,7 +2585,7 @@ impl Context {
|
|||
for (_id, window) in ctx.memory.areas().visible_windows() {
|
||||
used = used.union(window.rect());
|
||||
}
|
||||
used
|
||||
used.round_ui()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -2594,7 +2593,7 @@ impl Context {
|
|||
///
|
||||
/// You can shrink your egui area to this size and still fit all egui components.
|
||||
pub fn used_size(&self) -> Vec2 {
|
||||
self.used_rect().max - Pos2::ZERO
|
||||
(self.used_rect().max - Pos2::ZERO).round_ui()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use emath::GuiRounding as _;
|
||||
|
||||
use crate::{
|
||||
vec2, Align2, Color32, Context, Id, InnerResponse, NumExt, Painter, Rect, Region, Style, Ui,
|
||||
UiBuilder, Vec2,
|
||||
|
|
@ -179,13 +181,15 @@ impl GridLayout {
|
|||
let width = self.prev_state.col_width(self.col).unwrap_or(0.0);
|
||||
let height = self.prev_row_height(self.row);
|
||||
let size = child_size.max(vec2(width, height));
|
||||
Rect::from_min_size(cursor.min, size)
|
||||
Rect::from_min_size(cursor.min, size).round_ui()
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
pub(crate) fn align_size_within_rect(&self, size: Vec2, frame: Rect) -> Rect {
|
||||
// TODO(emilk): allow this alignment to be customized
|
||||
Align2::LEFT_CENTER.align_size_within_rect(size, frame)
|
||||
Align2::LEFT_CENTER
|
||||
.align_size_within_rect(size, frame)
|
||||
.round_ui()
|
||||
}
|
||||
|
||||
pub(crate) fn justify_and_align(&self, frame: Rect, size: Vec2) -> Rect {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use emath::GuiRounding as _;
|
||||
|
||||
use crate::{
|
||||
emath::{pos2, vec2, Align2, NumExt, Pos2, Rect, Vec2},
|
||||
Align,
|
||||
|
|
@ -394,7 +396,7 @@ impl Layout {
|
|||
pub fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect {
|
||||
debug_assert!(size.x >= 0.0 && size.y >= 0.0);
|
||||
debug_assert!(!outer.is_negative());
|
||||
self.align2().align_size_within_rect(size, outer)
|
||||
self.align2().align_size_within_rect(size, outer).round_ui()
|
||||
}
|
||||
|
||||
fn initial_cursor(&self, max_rect: Rect) -> Rect {
|
||||
|
|
@ -634,7 +636,7 @@ impl Layout {
|
|||
debug_assert!(!frame_rect.any_nan());
|
||||
debug_assert!(!frame_rect.is_negative());
|
||||
|
||||
frame_rect
|
||||
frame_rect.round_ui()
|
||||
}
|
||||
|
||||
/// Apply justify (fill width/height) and/or alignment after calling `next_space`.
|
||||
|
|
|
|||
|
|
@ -1168,11 +1168,9 @@ pub struct DebugOptions {
|
|||
/// Show interesting widgets under the mouse cursor.
|
||||
pub show_widget_hits: bool,
|
||||
|
||||
/// If true, highlight widgets that are not aligned to integer point coordinates.
|
||||
/// If true, highlight widgets that are not aligned to [`emath::GUI_ROUNDING`].
|
||||
///
|
||||
/// It's usually a good idea to keep to integer coordinates to avoid rounding issues.
|
||||
///
|
||||
/// See <https://github.com/emilk/egui/issues/5163> for more.
|
||||
/// See [`emath::GuiRounding`] for more.
|
||||
pub show_unaligned: bool,
|
||||
}
|
||||
|
||||
|
|
@ -1189,7 +1187,7 @@ impl Default for DebugOptions {
|
|||
show_resize: false,
|
||||
show_interactive_widgets: false,
|
||||
show_widget_hits: false,
|
||||
show_unaligned: false,
|
||||
show_unaligned: cfg!(debug_assertions),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use std::{any::Any, hash::Hash, sync::Arc};
|
||||
|
||||
use emath::GuiRounding as _;
|
||||
use epaint::mutex::RwLock;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -716,7 +717,9 @@ impl Ui {
|
|||
self.painter().layer_id()
|
||||
}
|
||||
|
||||
/// The height of text of this text style
|
||||
/// The height of text of this text style.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
pub fn text_style_height(&self, style: &TextStyle) -> f32 {
|
||||
self.fonts(|f| f.row_height(&style.resolve(self.style())))
|
||||
}
|
||||
|
|
@ -1295,6 +1298,7 @@ impl Ui {
|
|||
/// Ignore the layout of the [`Ui`]: just put my widget here!
|
||||
/// The layout cursor will advance to past this `rect`.
|
||||
pub fn allocate_rect(&mut self, rect: Rect, sense: Sense) -> Response {
|
||||
let rect = rect.round_ui();
|
||||
let id = self.advance_cursor_after_rect(rect);
|
||||
self.interact(rect, id, sense)
|
||||
}
|
||||
|
|
@ -1302,6 +1306,8 @@ impl Ui {
|
|||
/// Allocate a rect without interacting with it.
|
||||
pub fn advance_cursor_after_rect(&mut self, rect: Rect) -> Id {
|
||||
debug_assert!(!rect.any_nan());
|
||||
let rect = rect.round_ui();
|
||||
|
||||
let item_spacing = self.spacing().item_spacing;
|
||||
self.placer.advance_after_rects(rect, rect, item_spacing);
|
||||
register_rect(self, rect);
|
||||
|
|
@ -3018,7 +3024,7 @@ impl Drop for Ui {
|
|||
/// Show this rectangle to the user if certain debug options are set.
|
||||
#[cfg(debug_assertions)]
|
||||
fn register_rect(ui: &Ui, rect: Rect) {
|
||||
use emath::Align2;
|
||||
use emath::{Align2, GuiRounding};
|
||||
|
||||
let debug = ui.style().debug;
|
||||
|
||||
|
|
@ -3031,16 +3037,16 @@ fn register_rect(ui: &Ui, rect: Rect) {
|
|||
.text(p0, Align2::LEFT_TOP, "Unaligned", font_id, color);
|
||||
};
|
||||
|
||||
if rect.left().fract() != 0.0 {
|
||||
if rect.left() != rect.left().round_ui() {
|
||||
unaligned_line(rect.left_top(), rect.left_bottom());
|
||||
}
|
||||
if rect.right().fract() != 0.0 {
|
||||
if rect.right() != rect.right().round_ui() {
|
||||
unaligned_line(rect.right_top(), rect.right_bottom());
|
||||
}
|
||||
if rect.top().fract() != 0.0 {
|
||||
if rect.top() != rect.top().round_ui() {
|
||||
unaligned_line(rect.left_top(), rect.right_top());
|
||||
}
|
||||
if rect.bottom().fract() != 0.0 {
|
||||
if rect.bottom() != rect.bottom().round_ui() {
|
||||
unaligned_line(rect.left_bottom(), rect.right_bottom());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use emath::GuiRounding as _;
|
||||
|
||||
use crate::{
|
||||
text::{LayoutJob, TextWrapping},
|
||||
Align, Color32, FontFamily, FontSelection, Galley, Style, TextStyle, TextWrapMode, Ui, Visuals,
|
||||
|
|
@ -278,6 +280,8 @@ impl RichText {
|
|||
}
|
||||
|
||||
/// Read the font height of the selected text style.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
pub fn font_height(&self, fonts: &epaint::Fonts, style: &Style) -> f32 {
|
||||
let mut font_id = self.text_style.as_ref().map_or_else(
|
||||
|| FontSelection::Default.resolve(style),
|
||||
|
|
@ -635,15 +639,16 @@ impl WidgetText {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
pub(crate) fn font_height(&self, fonts: &epaint::Fonts, style: &Style) -> f32 {
|
||||
match self {
|
||||
Self::RichText(text) => text.font_height(fonts, style),
|
||||
Self::LayoutJob(job) => job.font_height(fonts),
|
||||
Self::Galley(galley) => {
|
||||
if let Some(row) = galley.rows.first() {
|
||||
row.height()
|
||||
row.height().round_ui()
|
||||
} else {
|
||||
galley.size().y
|
||||
galley.size().y.round_ui()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -681,7 +681,7 @@ fn mul_color_gamma(left: Color32, right: Color32) -> Color32 {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ColorTest;
|
||||
use egui::vec2;
|
||||
use egui_kittest::kittest::Queryable as _;
|
||||
|
||||
#[test]
|
||||
pub fn rendering_test() {
|
||||
|
|
@ -689,13 +689,16 @@ mod tests {
|
|||
for dpi in [1.0, 1.25, 1.5, 1.75, 1.6666667, 2.0] {
|
||||
let mut color_test = ColorTest::default();
|
||||
let mut harness = egui_kittest::Harness::builder()
|
||||
.with_size(vec2(2000.0, 2000.0))
|
||||
.with_pixels_per_point(dpi)
|
||||
.build_ui(|ui| {
|
||||
color_test.ui(ui);
|
||||
});
|
||||
|
||||
//harness.set_size(harness.ctx.used_size());
|
||||
{
|
||||
// Expand color-test collapsing header
|
||||
harness.get_by_label("Color test").click();
|
||||
harness.run();
|
||||
}
|
||||
|
||||
harness.fit_contents();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:01aaa4ef1a167a94fa1e5163550aabe4fa5e9f3a012b26170fe3088a6ca32d94
|
||||
size 81064
|
||||
oid sha256:f93cbf4968e69e2b51256631ab8703d1af2274147924f8d802d9da0f9767aa1b
|
||||
size 81162
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:684648bea4ef5ce138fc25dbe7576e3937a797e87f2244cb3656ff8b4c2777f5
|
||||
size 11574
|
||||
oid sha256:b7157cc3a1c87b63a1af41e9c3493beb426f9a2c7e3ce1adf228a8d1aee23818
|
||||
size 11549
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ad38bff7cc5661be43e730e1b34c444b571b24b9f50791209496a1687610dd3d
|
||||
size 20543
|
||||
oid sha256:7fcc5ac195fdf455836cce7503c530d56a5c9e053cf743f8165c2b33212bfaae
|
||||
size 20645
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ff78748f2571c49638d8fe8fdc859aaa5181758aad65498b7217551350fb9138
|
||||
size 20672
|
||||
oid sha256:eee96c06b54b0c9779a84bce1caf94f907593d675625a51791b18925881bd619
|
||||
size 20883
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9dee66004cc47f5e27aaac34d137ff005eedf70cbfa3fbe43153dfd5c09d5e18
|
||||
size 10610
|
||||
oid sha256:cced772c3830cf1d8d5f946cb457f1a0a059ac3b08b791899db5f5fe8132fc85
|
||||
size 10711
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0d1086b789f1fe0a8085c86f5b6a5ae7ecb53020f385b84775d6812ebc9d74a3
|
||||
size 132349
|
||||
oid sha256:cce41bc2887462a0a3aebc4148faf769d3666ff4a0e4c7db6ffbddb174be0ed3
|
||||
size 135734
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:08be378c01e376aab6e99ba3158519bbd7b301e815dc3447b57c9abab558977f
|
||||
size 24237
|
||||
oid sha256:194c07bce8370b886b1bcb58936044e71f4725b2034dff3bbb7e3f710cf82f17
|
||||
size 24701
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:53097b2c26ebcba8b8ad657ed8e52ca40261155e96dbbfca1e8eb01fce25d290
|
||||
size 17586
|
||||
oid sha256:1b5a71ea8cd6b3e2b267d668c1f149689e938eef3b4038a0935823d37aba348f
|
||||
size 17806
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:38d21b6f8c364f86ad759e88ea1068649c23c58ded5d2953ba8ff1c83b46112f
|
||||
size 63884
|
||||
oid sha256:17901f3e235cfbac03fb2334f36f614ed152a25b3a212e78beba9fb342039844
|
||||
size 65339
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:83162f8c496a55230375dbc4cc636cfacf63049c913904bea9d06bdb56e63da6
|
||||
size 36282
|
||||
oid sha256:950c825c8f6eadc682c57fe8843ded2ceb6981b6d4086d427dcf0d27740df8ef
|
||||
size 36795
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2537c681d1ffceb5cf4bf19d11295891525c96aea0b1422ab28f133021185be0
|
||||
size 17451
|
||||
oid sha256:e68f145f57812a1f6a81e1a8ab41bcb134320dcaaee3f462d1aa00d6c331752a
|
||||
size 17579
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ccfda16ef7cdf94f7fbbd2c0f8df6f6de7904969e2a66337920c32608a6f9f05
|
||||
size 25357
|
||||
oid sha256:6aec5a6291ad2796457d1c4fc44228c75bf075262aa672749a3cceb4baef1e08
|
||||
size 25271
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:be2ac005fd5aafa293e21b162c22a09078e46d2d45b6208ce0f7841eeb05314a
|
||||
size 183934
|
||||
oid sha256:4c50f7cbac65ff98b4bdb11034dd18bf19836ea22f27e0ed04415ac763d86d09
|
||||
size 188067
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:df7dabf726620ab5205ce153f692d1ba02365848ead7b79c95b873d5121d52a6
|
||||
size 25850
|
||||
oid sha256:764fc4f5fc60cc72d9731c7073e05abda70c1b5caf2992aabad63772e0cd5e33
|
||||
size 25905
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ae6c2e3aad43cfad3322340ff7045ec50ba01d58feb7b8acc5ba062a8a5c9ab8
|
||||
size 70230
|
||||
oid sha256:cce44cee74da69ce065e79f92919928bbdca6b1e57b44f0165666508b3cddce3
|
||||
size 72240
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ec0c2efff75cb8d621f5a4ea59f9fa8d3076521ca34f4499e07fb9dc8681d7ba
|
||||
size 65916
|
||||
oid sha256:10d278417ae66a788a88676874381b2d7c026b3130ca1c9178e2027595bb8e74
|
||||
size 67383
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c04aee0a3a77a3691bb601a93871117500be917e0896138fda43251454ec04c2
|
||||
size 20988
|
||||
oid sha256:6f13c687e27870df62488563de07b02711eb508ec7eee91470a44f2669610889
|
||||
size 21342
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e682f5cb9ecb1bdf89281c2ba1612078e70e97f28c76facc64d717e4015ced6a
|
||||
size 12977
|
||||
oid sha256:65674a0da6ee80eff735a6404ad328332ad0ab48766de9b1d358e38ccb3ecc29
|
||||
size 13059
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:15acfb041cc53ef9bd966d6edd53a6b692cdb645ae5cf34bc20e70d403371c30
|
||||
size 34809
|
||||
oid sha256:450ede959fac122e6b045d18a6ab62952fa03371508704a1f2303d6379e41593
|
||||
size 35523
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8348ff582e11fdc9baf008b5434f81f8d77b834479cb3765c87d1f4fd695e30f
|
||||
size 48212
|
||||
oid sha256:bd53d25bcecc4f22cb9226237d69557696e8aa08b969157c0cdb015cfacf206d
|
||||
size 48244
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:23482b77cbd817c66421a630e409ac3d8c5d24de00aa91e476e8d42b607c24b1
|
||||
size 48104
|
||||
oid sha256:d9d9226996b0eecf516b9b89f09c57e5f7ad43cc154a2c8937779421a6580113
|
||||
size 48157
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2d94aa33d72c32f6f1aafab92c9753dc07bc5224c701003ac7fe8a01ae8c701a
|
||||
size 44011
|
||||
oid sha256:49401fcd6e348c75d81ad7419e1fa1a77500ca3d2360b418e8304ad5843e5b3a
|
||||
size 44080
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e1a5d265470c36e64340ccceea4ade464b3c4a1177d60630b02ae8287934748f
|
||||
size 44026
|
||||
oid sha256:6cc1363890eb878ec7f37bf5350dd5a81ed39e16655b2e8b7fee0937e605bcae
|
||||
size 44044
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:023eaa363b42ec24ae845dc2ca9ff271a0bd47217e625785d3716044ecfa7a64
|
||||
size 278444
|
||||
oid sha256:dc83a67ac7ff574f981dae02c05ecfba934df4fb181bba12fd7607ef9cba5aa6
|
||||
size 523650
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d81f618e54176b1c43b710121f249e13ce29827fbea3451827ab62229006677e
|
||||
size 378603
|
||||
oid sha256:a0fe7626d08a4d28a66d39824cdcd8fef140c7c818e5fc07a7e86756c249875d
|
||||
size 737802
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2d8eca6d5555ef779233175615b877fb91318b4a09a37e5cfbe71973d56f4caf
|
||||
size 465907
|
||||
oid sha256:477437c5c8766beac88a96f531d8dfe5fac1fe2cdd4bad02fbc53610eb8925fb
|
||||
size 887154
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4768804f57dfc54c5f6b84a2686038b8d630a28c7e928ae044d5b2ce8377e2cd
|
||||
size 538775
|
||||
oid sha256:8aa0503d6e5dc82953000c7e68be826b0bf4f288aef82c2d5d832189538eb3b2
|
||||
size 1002104
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fcee0e0302f33d681348d62bee3b548beb494c6dd1fa3454586986e0b699e162
|
||||
size 572403
|
||||
oid sha256:bef9f6dde686733e04a447be280fd50704db163d330fd8c663357b057cd50e27
|
||||
size 1080661
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:254a8dff0b1d4b74971fd3bd4044c4ec0ce49412a95e98419a14dc55b32a4fc9
|
||||
size 663272
|
||||
oid sha256:136c60283286df17e6751220e0c849028bc1379910995cf9e4930c5d63d0a90f
|
||||
size 1235057
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c069ef4f86beeeafd8686f30fc914bedd7e7e7ec38fd96e9a46ac6b31308c43f
|
||||
size 160883
|
||||
oid sha256:9da93a7c27e17d370a3df73930d71694d436c300656b054722646ea57db45810
|
||||
size 160803
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use egui::{Id, Pos2, Rect, Response, Sense, Ui, UiBuilder};
|
||||
use egui::{emath::GuiRounding, Id, Pos2, Rect, Response, Sense, Ui, UiBuilder};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum CellSize {
|
||||
|
|
@ -123,7 +123,7 @@ impl<'l> StripLayout<'l> {
|
|||
|
||||
// Make sure we don't have a gap in the stripe/frame/selection background:
|
||||
let item_spacing = self.ui.spacing().item_spacing;
|
||||
let gapless_rect = max_rect.expand2(0.5 * item_spacing);
|
||||
let gapless_rect = max_rect.expand2(0.5 * item_spacing).round_ui();
|
||||
|
||||
if flags.striped {
|
||||
self.ui.painter().rect_filled(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,201 @@
|
|||
/// We (sometimes) round sizes and coordinates to an even multiple of this value.
|
||||
///
|
||||
/// This is only used for rounding _logical UI points_, used for widget coordinates and sizes.
|
||||
/// When rendering, you may want to round to an integer multiple of the physical _pixels_ instead,
|
||||
/// using [`GuiRounding::round_to_pixels`].
|
||||
///
|
||||
/// See [`GuiRounding::round_ui`] for more information.
|
||||
///
|
||||
/// This constant has to be a (negative) power of two so that it can be represented exactly
|
||||
/// by a floating point number.
|
||||
///
|
||||
/// If we pick too large a value (e.g. 1 or 1/2), then we get judder during scrolling and animations.
|
||||
/// If we pick too small a value (e.g. 1/4096), we run the risk of rounding errors again.
|
||||
///
|
||||
/// `f32` has 23 bits of mantissa, so if we use e.g. 1/8 as the rounding factor,
|
||||
/// we can represent all numbers up to 2^20 exactly, which is plenty
|
||||
/// (to my knowledge there are no displays that are a million pixels wide).
|
||||
pub const GUI_ROUNDING: f32 = 1.0 / 32.0;
|
||||
|
||||
/// Trait for rounding coordinates and sizes to align with either .
|
||||
///
|
||||
/// See [`GuiRounding::round_ui`] for more information.
|
||||
pub trait GuiRounding {
|
||||
/// Rounds floating point numbers to an even multiple of the GUI rounding factor, [`crate::GUI_ROUNDING`].
|
||||
///
|
||||
/// Use this for widget coordinates and sizes.
|
||||
///
|
||||
/// Rounding sizes and positions prevent rounding errors when doing sizing calculations.
|
||||
/// We don't round to integers, because that would be too coarse (causing visible juddering when scrolling, for instance).
|
||||
/// Instead we round to an even multiple of [`GUI_ROUNDING`].
|
||||
fn round_ui(self) -> Self;
|
||||
|
||||
/// Like [`Self::round_ui`], but always rounds towards negative infinity.
|
||||
fn floor_ui(self) -> Self;
|
||||
|
||||
/// Round a size or position to an even multiple of the physical pixel size.
|
||||
///
|
||||
/// This can be useful for crisp rendering.
|
||||
///
|
||||
/// The `self` should be in coordinates of _logical UI points_.
|
||||
/// The argument `pixels_per_point` is the number of _physical pixels_ per logical UI point.
|
||||
/// For instance, on a high-DPI screen, `pixels_per_point` could be `2.0`.
|
||||
fn round_to_pixels(self, pixels_per_point: f32) -> Self;
|
||||
|
||||
/// Will round the position to be in the center of a pixel.
|
||||
///
|
||||
/// The pixel size is `1.0 / pixels_per_point`.
|
||||
///
|
||||
/// So if `pixels_per_point = 2` (i.e. `pixel size = 0.5`),
|
||||
/// then the position will be rounded to the closest of `…, 0.25, 0.75, 1.25, …`.
|
||||
///
|
||||
/// This is useful, for instance, when picking the center of a line that is one pixel wide.
|
||||
fn round_to_pixel_center(self, pixels_per_point: f32) -> Self;
|
||||
}
|
||||
|
||||
impl GuiRounding for f32 {
|
||||
#[inline]
|
||||
fn round_ui(self) -> Self {
|
||||
(self / GUI_ROUNDING).round() * GUI_ROUNDING
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn floor_ui(self) -> Self {
|
||||
(self / GUI_ROUNDING).floor() * GUI_ROUNDING
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_to_pixels(self, pixels_per_point: f32) -> Self {
|
||||
(self * pixels_per_point).round() / pixels_per_point
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {
|
||||
((self * pixels_per_point - 0.5).round() + 0.5) / pixels_per_point
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiRounding for f64 {
|
||||
#[inline]
|
||||
fn round_ui(self) -> Self {
|
||||
(self / GUI_ROUNDING as Self).round() * GUI_ROUNDING as Self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn floor_ui(self) -> Self {
|
||||
(self / GUI_ROUNDING as Self).floor() * GUI_ROUNDING as Self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_to_pixels(self, pixels_per_point: f32) -> Self {
|
||||
(self * pixels_per_point as Self).round() / pixels_per_point as Self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {
|
||||
((self * pixels_per_point as Self - 0.5).round() + 0.5) / pixels_per_point as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiRounding for crate::Vec2 {
|
||||
#[inline]
|
||||
fn round_ui(self) -> Self {
|
||||
Self::new(self.x.round_ui(), self.y.round_ui())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn floor_ui(self) -> Self {
|
||||
Self::new(self.x.floor_ui(), self.y.floor_ui())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_to_pixels(self, pixels_per_point: f32) -> Self {
|
||||
Self::new(
|
||||
self.x.round_to_pixels(pixels_per_point),
|
||||
self.y.round_to_pixels(pixels_per_point),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {
|
||||
Self::new(
|
||||
self.x.round_to_pixel_center(pixels_per_point),
|
||||
self.y.round_to_pixel_center(pixels_per_point),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiRounding for crate::Pos2 {
|
||||
#[inline]
|
||||
fn round_ui(self) -> Self {
|
||||
Self::new(self.x.round_ui(), self.y.round_ui())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn floor_ui(self) -> Self {
|
||||
Self::new(self.x.floor_ui(), self.y.floor_ui())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_to_pixels(self, pixels_per_point: f32) -> Self {
|
||||
Self::new(
|
||||
self.x.round_to_pixels(pixels_per_point),
|
||||
self.y.round_to_pixels(pixels_per_point),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {
|
||||
Self::new(
|
||||
self.x.round_to_pixel_center(pixels_per_point),
|
||||
self.y.round_to_pixel_center(pixels_per_point),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiRounding for crate::Rect {
|
||||
/// Rounded so that two adjacent rects that tile perfectly
|
||||
/// will continue to tile perfectly.
|
||||
#[inline]
|
||||
fn round_ui(self) -> Self {
|
||||
Self::from_min_max(self.min.round_ui(), self.max.round_ui())
|
||||
}
|
||||
|
||||
/// Rounded so that two adjacent rects that tile perfectly
|
||||
/// will continue to tile perfectly.
|
||||
#[inline]
|
||||
fn floor_ui(self) -> Self {
|
||||
Self::from_min_max(self.min.floor_ui(), self.max.floor_ui())
|
||||
}
|
||||
|
||||
/// Rounded so that two adjacent rects that tile perfectly
|
||||
/// will continue to tile perfectly.
|
||||
#[inline]
|
||||
fn round_to_pixels(self, pixels_per_point: f32) -> Self {
|
||||
Self::from_min_max(
|
||||
self.min.round_to_pixels(pixels_per_point),
|
||||
self.max.round_to_pixels(pixels_per_point),
|
||||
)
|
||||
}
|
||||
|
||||
/// Rounded so that two adjacent rects that tile perfectly
|
||||
/// will continue to tile perfectly.
|
||||
#[inline]
|
||||
fn round_to_pixel_center(self, pixels_per_point: f32) -> Self {
|
||||
Self::from_min_max(
|
||||
self.min.round_to_pixel_center(pixels_per_point),
|
||||
self.max.round_to_pixel_center(pixels_per_point),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gui_rounding() {
|
||||
assert_eq!(0.0_f32.round_ui(), 0.0);
|
||||
assert_eq!((GUI_ROUNDING * 1.11).round_ui(), GUI_ROUNDING);
|
||||
assert_eq!((-GUI_ROUNDING * 1.11).round_ui(), -GUI_ROUNDING);
|
||||
assert_eq!(f32::NEG_INFINITY.round_ui(), f32::NEG_INFINITY);
|
||||
assert_eq!(f32::INFINITY.round_ui(), f32::INFINITY);
|
||||
|
||||
assert_eq!(0.17_f32.round_to_pixel_center(2.0), 0.25);
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ use std::ops::{Add, Div, Mul, RangeInclusive, Sub};
|
|||
|
||||
pub mod align;
|
||||
pub mod easing;
|
||||
mod gui_rounding;
|
||||
mod history;
|
||||
mod numeric;
|
||||
mod ordered_float;
|
||||
|
|
@ -42,6 +43,7 @@ mod vec2b;
|
|||
|
||||
pub use self::{
|
||||
align::{Align, Align2},
|
||||
gui_rounding::{GuiRounding, GUI_ROUNDING},
|
||||
history::History,
|
||||
numeric::*,
|
||||
ordered_float::*,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use emath::{vec2, Vec2};
|
||||
use emath::{vec2, GuiRounding, Vec2};
|
||||
|
||||
use crate::{
|
||||
mutex::{Mutex, RwLock},
|
||||
|
|
@ -96,22 +96,18 @@ impl FontImpl {
|
|||
|
||||
use ab_glyph::{Font, ScaleFont};
|
||||
let scaled = ab_glyph_font.as_scaled(scale_in_pixels);
|
||||
let ascent = scaled.ascent() / pixels_per_point;
|
||||
let descent = scaled.descent() / pixels_per_point;
|
||||
let line_gap = scaled.line_gap() / pixels_per_point;
|
||||
let ascent = (scaled.ascent() / pixels_per_point).round_ui();
|
||||
let descent = (scaled.descent() / pixels_per_point).round_ui();
|
||||
let line_gap = (scaled.line_gap() / pixels_per_point).round_ui();
|
||||
|
||||
// Tweak the scale as the user desired
|
||||
let scale_in_pixels = scale_in_pixels * tweak.scale;
|
||||
let scale_in_points = scale_in_pixels / pixels_per_point;
|
||||
|
||||
let baseline_offset = {
|
||||
let scale_in_points = scale_in_pixels / pixels_per_point;
|
||||
scale_in_points * tweak.baseline_offset_factor
|
||||
};
|
||||
let baseline_offset = (scale_in_points * tweak.baseline_offset_factor).round_ui();
|
||||
|
||||
let y_offset_points = {
|
||||
let scale_in_points = scale_in_pixels / pixels_per_point;
|
||||
scale_in_points * tweak.y_offset_factor
|
||||
} + tweak.y_offset;
|
||||
let y_offset_points =
|
||||
((scale_in_points * tweak.y_offset_factor) + tweak.y_offset).round_ui();
|
||||
|
||||
// Center scaled glyphs properly:
|
||||
let height = ascent + descent;
|
||||
|
|
@ -247,6 +243,8 @@ impl FontImpl {
|
|||
}
|
||||
|
||||
/// Height of one row of text in points.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
#[inline(always)]
|
||||
pub fn row_height(&self) -> f32 {
|
||||
self.height_in_points
|
||||
|
|
@ -418,7 +416,9 @@ impl Font {
|
|||
(point * self.pixels_per_point).round() / self.pixels_per_point
|
||||
}
|
||||
|
||||
/// Height of one row of text. In points
|
||||
/// Height of one row of text. In points.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
#[inline(always)]
|
||||
pub fn row_height(&self) -> f32 {
|
||||
self.row_height
|
||||
|
|
|
|||
|
|
@ -519,7 +519,9 @@ impl Fonts {
|
|||
self.lock().fonts.has_glyphs(font_id, s)
|
||||
}
|
||||
|
||||
/// Height of one row of text in points
|
||||
/// Height of one row of text in points.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
#[inline]
|
||||
pub fn row_height(&self, font_id: &FontId) -> f32 {
|
||||
self.lock().fonts.row_height(font_id)
|
||||
|
|
@ -706,6 +708,8 @@ impl FontsImpl {
|
|||
}
|
||||
|
||||
/// Height of one row of text in points.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
fn row_height(&mut self, font_id: &FontId) -> f32 {
|
||||
self.font(font_id).row_height()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use std::ops::RangeInclusive;
|
||||
use std::sync::Arc;
|
||||
|
||||
use emath::{pos2, vec2, Align, NumExt, Pos2, Rect, Vec2};
|
||||
use emath::{pos2, vec2, Align, GuiRounding as _, NumExt, Pos2, Rect, Vec2};
|
||||
|
||||
use crate::{stroke::PathStroke, text::font::Font, Color32, Mesh, Stroke, Vertex};
|
||||
|
||||
|
|
@ -630,7 +630,7 @@ fn galley_from_rows(
|
|||
min_x = min_x.min(row.rect.min.x);
|
||||
max_x = max_x.max(row.rect.max.x);
|
||||
cursor_y += max_row_height;
|
||||
cursor_y = point_scale.round_to_pixel(cursor_y);
|
||||
cursor_y = point_scale.round_to_pixel(cursor_y); // TODO(emilk): it would be better to do the calculations in pixels instead.
|
||||
}
|
||||
|
||||
let format_summary = format_summary(&job);
|
||||
|
|
@ -648,20 +648,25 @@ fn galley_from_rows(
|
|||
|
||||
let mut rect = Rect::from_min_max(pos2(min_x, 0.0), pos2(max_x, cursor_y));
|
||||
|
||||
if job.round_output_size_to_nearest_ui_point {
|
||||
if job.round_output_to_gui {
|
||||
for row in &mut rows {
|
||||
row.rect = row.rect.round_ui();
|
||||
}
|
||||
|
||||
let did_exceed_wrap_width_by_a_lot = rect.width() > job.wrap.max_width + 1.0;
|
||||
|
||||
// We round the size to whole ui points here (not pixels!) so that the egui layout code
|
||||
// can have the advantage of working in integer units, avoiding rounding errors.
|
||||
rect.min = rect.min.round();
|
||||
rect.max = rect.max.round();
|
||||
rect = rect.round_ui();
|
||||
|
||||
if did_exceed_wrap_width_by_a_lot {
|
||||
// If the user picked a too aggressive wrap width (e.g. more narrow than any individual glyph),
|
||||
// we should let the user know by reporting that our width is wider than the wrap width.
|
||||
} else {
|
||||
// Make sure we don't report being wider than the wrap width the user picked:
|
||||
rect.max.x = rect.max.x.at_most(rect.min.x + job.wrap.max_width).floor();
|
||||
rect.max.x = rect
|
||||
.max
|
||||
.x
|
||||
.at_most(rect.min.x + job.wrap.max_width)
|
||||
.floor_ui();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1133,6 +1138,7 @@ mod tests {
|
|||
LayoutJob::single_section("# DNA\nMore text".into(), TextFormat::default());
|
||||
layout_job.wrap.max_width = f32::INFINITY;
|
||||
layout_job.wrap.max_rows = 1;
|
||||
layout_job.round_output_to_gui = false;
|
||||
let galley = layout(&mut fonts, layout_job.into());
|
||||
assert!(galley.elided);
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -78,9 +78,8 @@ pub struct LayoutJob {
|
|||
/// Justify text so that word-wrapped rows fill the whole [`TextWrapping::max_width`].
|
||||
pub justify: bool,
|
||||
|
||||
/// Rounding to the closest ui point (not pixel!) allows the rest of the
|
||||
/// layout code to run on perfect integers, avoiding rounding errors.
|
||||
pub round_output_size_to_nearest_ui_point: bool,
|
||||
/// Round output sizes using [`emath::GuiRounding`], to avoid rounding errors in layout code.
|
||||
pub round_output_to_gui: bool,
|
||||
}
|
||||
|
||||
impl Default for LayoutJob {
|
||||
|
|
@ -94,7 +93,7 @@ impl Default for LayoutJob {
|
|||
break_on_newline: true,
|
||||
halign: Align::LEFT,
|
||||
justify: false,
|
||||
round_output_size_to_nearest_ui_point: true,
|
||||
round_output_to_gui: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -168,6 +167,8 @@ impl LayoutJob {
|
|||
}
|
||||
|
||||
/// The height of the tallest font used in the job.
|
||||
///
|
||||
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
|
||||
pub fn font_height(&self, fonts: &crate::Fonts) -> f32 {
|
||||
let mut max_height = 0.0_f32;
|
||||
for section in &self.sections {
|
||||
|
|
@ -178,7 +179,7 @@ impl LayoutJob {
|
|||
|
||||
/// The wrap with, with a small margin in some cases.
|
||||
pub fn effective_wrap_width(&self) -> f32 {
|
||||
if self.round_output_size_to_nearest_ui_point {
|
||||
if self.round_output_to_gui {
|
||||
// On a previous pass we may have rounded down by at most 0.5 and reported that as a width.
|
||||
// egui may then set that width as the max width for subsequent frames, and it is important
|
||||
// that we then don't wrap earlier.
|
||||
|
|
@ -200,7 +201,7 @@ impl std::hash::Hash for LayoutJob {
|
|||
break_on_newline,
|
||||
halign,
|
||||
justify,
|
||||
round_output_size_to_nearest_ui_point,
|
||||
round_output_to_gui,
|
||||
} = self;
|
||||
|
||||
text.hash(state);
|
||||
|
|
@ -210,7 +211,7 @@ impl std::hash::Hash for LayoutJob {
|
|||
break_on_newline.hash(state);
|
||||
halign.hash(state);
|
||||
justify.hash(state);
|
||||
round_output_size_to_nearest_ui_point.hash(state);
|
||||
round_output_to_gui.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue