From f1db1f3a3c933d8b4d35d83f6bbc49fd46749519 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Mon, 13 Oct 2025 09:49:07 -0400 Subject: [PATCH] windows: Fix ascent/descent calculations (#40103) This applies the same fix as #39886 for Windows. Previously we were using `GetLineMetrics` to determine the ascent and descent values for each line. It seems like this has the same behavior as `GetTypographicBounds` on macOS, which is to return the minimum ascent and descent for the current state of the `TextLayout` object. This causes the ascent/descent to be unstable when adding or removing an emoji because a font fallback is triggered when an emoji is present on the line. The issue is fixed by switching to `font.GetMetrics` to get the ascent and descent, which should always return stable values for the main font, instead of changing when there's a fallback. This also should support situations where we have multiple explicit fonts on the same line, although that probably can't be triggered in Zed right now. Release Notes: - windows: Fixed a vertical shift in text layout when inserting or removing an emoji. Co-authored-by: Bennet Bo Fenner --- .../gpui/src/platform/windows/direct_write.rs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/gpui/src/platform/windows/direct_write.rs b/crates/gpui/src/platform/windows/direct_write.rs index e187fc4b09176906102a1bf8fe50b410aae3cb2b..024b38b46a377982acc034dac4aeb81967f8feaa 100644 --- a/crates/gpui/src/platform/windows/direct_write.rs +++ b/crates/gpui/src/platform/windows/direct_write.rs @@ -606,19 +606,21 @@ impl DirectWriteState { }; let mut first_run = true; - let mut ascent = Pixels::default(); - let mut descent = Pixels::default(); + let mut max_ascent = 0.0_f32; + let mut max_descent = 0.0_f32; for run in font_runs { + let font_info = &self.fonts[run.font_id.0]; + let mut metrics = std::mem::zeroed(); + font_info.font_face.GetMetrics(&mut metrics); + let font_scale = font_size.0 / metrics.Base.designUnitsPerEm as f32; + max_ascent = max_ascent.max(metrics.Base.ascent as f32 * font_scale); + max_descent = max_descent.max(-(metrics.Base.descent as f32 * font_scale)); + if first_run { first_run = false; - let mut metrics = vec![DWRITE_LINE_METRICS::default(); 4]; - let mut line_count = 0u32; - text_layout.GetLineMetrics(Some(&mut metrics), &mut line_count as _)?; - ascent = px(metrics[0].baseline); - descent = px(metrics[0].height - metrics[0].baseline); continue; } - let font_info = &self.fonts[run.font_id.0]; + let current_text = &text[utf8_offset..(utf8_offset + run.len)]; utf8_offset += run.len; let current_text_utf16_length = current_text.encode_utf16().count() as u32; @@ -660,8 +662,8 @@ impl DirectWriteState { Ok(LineLayout { font_size, width, - ascent, - descent, + ascent: max_ascent.into(), + descent: max_descent.into(), runs, len: text.len(), })