From e9f4fcc7f68aab689ea40abc2bc02b062c903797 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 25 Sep 2024 11:31:41 +0200 Subject: [PATCH] Prevent text shrinking in tooltips; round wrap-width to integer (#5161) * Closes https://github.com/emilk/egui/pull/5106 * Closes https://github.com/emilk/egui/issues/5084 Protect against rounding errors in egui layout code. Say the user asks to wrap at width 200.0. The text layout wraps, and reports that the final width was 196.0 points. This than trickles up the `Ui` chain and gets stored as the width for a tooltip (say). On the next frame, this is then set as the max width for the tooltip, and we end up calling the text layout code again, this time with a wrap width of 196.0. Except, somewhere in the `Ui` chain with added margins etc, a rounding error was introduced, so that we actually set a wrap-width of 195.9997 instead. Now the text that fit perfectly at 196.0 needs to wrap one word earlier, and so the text re-wraps and reports a new width of 185.0 points. And then the cycle continues. So this PR limits the text wrap-width to be an integer. Related issues: * https://github.com/emilk/egui/issues/4927 * https://github.com/emilk/egui/issues/4928 * https://github.com/emilk/egui/issues/5163 --- Pleas test this @rustbasic --- crates/epaint/src/text/fonts.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index 7eaff1c8875..42398e5f4db 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -676,7 +676,32 @@ struct GalleyCache { } impl GalleyCache { - fn layout(&mut self, fonts: &mut FontsImpl, job: LayoutJob) -> Arc { + fn layout(&mut self, fonts: &mut FontsImpl, mut job: LayoutJob) -> Arc { + if job.wrap.max_width.is_finite() { + // Protect against rounding errors in egui layout code. + + // Say the user asks to wrap at width 200.0. + // The text layout wraps, and reports that the final width was 196.0 points. + // This than trickles up the `Ui` chain and gets stored as the width for a tooltip (say). + // On the next frame, this is then set as the max width for the tooltip, + // and we end up calling the text layout code again, this time with a wrap width of 196.0. + // Except, somewhere in the `Ui` chain with added margins etc, a rounding error was introduced, + // so that we actually set a wrap-width of 195.9997 instead. + // Now the text that fit perfrectly at 196.0 needs to wrap one word earlier, + // and so the text re-wraps and reports a new width of 185.0 points. + // And then the cycle continues. + + // So we limit max_width to integers. + + // Related issues: + // * https://github.com/emilk/egui/issues/4927 + // * https://github.com/emilk/egui/issues/4928 + // * https://github.com/emilk/egui/issues/5084 + // * https://github.com/emilk/egui/issues/5163 + + job.wrap.max_width = job.wrap.max_width.round(); + } + let hash = crate::util::hash(&job); // TODO(emilk): even faster hasher? match self.cache.entry(hash) {