Scale UI elements in the editor based on the buffer_font_size (#11817)

This PR adjusts how UI elements are rendered inside of full-size editors
to scale with the configured `buffer_font_size`.

This fixes some issues where UI elements (such as the `IconButton`s used
for code action and task run indicators) would not scale as the
`buffer_font_size` was changed.

We achieve this by changing the rem size when rendering the
`EditorElement`, with a rem size that is derived from the
`buffer_font_size`.

`WindowContext` now has a new `with_rem_size` method that can be used to
render an element with a given rem size. Note that this can only be
called during `request_layout`, `prepaint`, or `paint`, similar to
`with_text_style` or `with_content_mask`.

### Before

<img width="1264" alt="Screenshot 2024-05-14 at 2 15 39 PM"
src="https://github.com/zed-industries/zed/assets/1486634/05ad7f8d-c62f-4baa-bffd-38cace7f3710">

<img width="1264" alt="Screenshot 2024-05-14 at 2 15 49 PM"
src="https://github.com/zed-industries/zed/assets/1486634/254cd11c-3723-488f-ab3d-ed653169056c">

### After

<img width="1264" alt="Screenshot 2024-05-14 at 2 13 02 PM"
src="https://github.com/zed-industries/zed/assets/1486634/c8dad309-62a4-444f-bfeb-a0009dc08c03">

<img width="1264" alt="Screenshot 2024-05-14 at 2 13 06 PM"
src="https://github.com/zed-industries/zed/assets/1486634/4d9a3a52-9656-4768-b210-840b4884e381">

Note: This diff is best viewed with whitespace changes hidden:

<img width="245" alt="Screenshot 2024-05-14 at 2 22 45 PM"
src="https://github.com/zed-industries/zed/assets/1486634/7cb9829f-9c1b-4224-95be-82182017ed90">

Release Notes:

- Changed UI elements within the editor to scale based on
`buffer_font_size` (e.g., code action indicators, task run indicators,
etc.).
This commit is contained in:
Marshall Bowers 2024-05-14 14:34:39 -04:00 committed by GitHub
parent c8ddde27e1
commit 5b8bb6237f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 560 additions and 479 deletions

View file

@ -58,7 +58,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
use sum_tree::Bias; use sum_tree::Bias;
use theme::{ActiveTheme, PlayerColor}; use theme::{ActiveTheme, PlayerColor, ThemeSettings};
use ui::prelude::*; use ui::prelude::*;
use ui::{h_flex, ButtonLike, ButtonStyle, ContextMenu, Tooltip}; use ui::{h_flex, ButtonLike, ButtonStyle, ContextMenu, Tooltip};
use util::ResultExt; use util::ResultExt;
@ -3705,6 +3705,38 @@ enum Invisible {
Whitespace { line_offset: usize }, Whitespace { line_offset: usize },
} }
impl EditorElement {
/// Returns the rem size to use when rendering the [`EditorElement`].
///
/// This allows UI elements to scale based on the `buffer_font_size`.
fn rem_size(&self, cx: &WindowContext) -> Option<Pixels> {
match self.editor.read(cx).mode {
EditorMode::Full => {
let buffer_font_size = ThemeSettings::get_global(cx).buffer_font_size;
let rem_size_scale = {
// Our default UI font size is 14px on a 16px base scale.
// This means the default UI font size is 0.875rems.
let default_font_size_scale = 14. / ui::BASE_REM_SIZE_IN_PX;
// We then determine the delta between a single rem and the default font
// size scale.
let default_font_size_delta = 1. - default_font_size_scale;
// Finally, we add this delta to 1rem to get the scale factor that
// should be used to scale up the UI.
1. + default_font_size_delta
};
Some(buffer_font_size * rem_size_scale)
}
// We currently use single-line and auto-height editors in UI contexts,
// so we don't want to scale everything with the buffer font size, as it
// ends up looking off.
EditorMode::SingleLine | EditorMode::AutoHeight { .. } => None,
}
}
}
impl Element for EditorElement { impl Element for EditorElement {
type RequestLayoutState = (); type RequestLayoutState = ();
type PrepaintState = EditorLayout; type PrepaintState = EditorLayout;
@ -3718,6 +3750,8 @@ impl Element for EditorElement {
_: Option<&GlobalElementId>, _: Option<&GlobalElementId>,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> (gpui::LayoutId, ()) { ) -> (gpui::LayoutId, ()) {
let rem_size = self.rem_size(cx);
cx.with_rem_size(rem_size, |cx| {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx); editor.set_style(self.style.clone(), cx);
@ -3761,6 +3795,7 @@ impl Element for EditorElement {
(layout_id, ()) (layout_id, ())
}) })
})
} }
fn prepaint( fn prepaint(
@ -3776,6 +3811,9 @@ impl Element for EditorElement {
..Default::default() ..Default::default()
}; };
cx.set_view_id(self.editor.entity_id()); cx.set_view_id(self.editor.entity_id());
let rem_size = self.rem_size(cx);
cx.with_rem_size(rem_size, |cx| {
cx.with_text_style(Some(text_style), |cx| { cx.with_text_style(Some(text_style), |cx| {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| { cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
let mut snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx)); let mut snapshot = self.editor.update(cx, |editor, cx| editor.snapshot(cx));
@ -3868,7 +3906,8 @@ impl Element for EditorElement {
self.editor.update(cx, |editor, cx| { self.editor.update(cx, |editor, cx| {
autoscroll_containing_element = autoscroll_containing_element =
editor.autoscroll_requested() || editor.has_pending_selection(); editor.autoscroll_requested() || editor.has_pending_selection();
autoscroll_horizontally = editor.autoscroll_vertically(bounds, line_height, cx); autoscroll_horizontally =
editor.autoscroll_vertically(bounds, line_height, cx);
snapshot = editor.snapshot(cx); snapshot = editor.snapshot(cx);
}); });
@ -3954,7 +3993,8 @@ impl Element for EditorElement {
} }
} }
let longest_line_width = layout_line(snapshot.longest_row(), &snapshot, &style, cx) let longest_line_width =
layout_line(snapshot.longest_row(), &snapshot, &style, cx)
.unwrap() .unwrap()
.width; .width;
let mut scroll_width = let mut scroll_width =
@ -4110,9 +4150,9 @@ impl Element for EditorElement {
if gutter_settings.code_actions { if gutter_settings.code_actions {
let newest_selection_point = let newest_selection_point =
newest_selection_head.to_point(&snapshot.display_snapshot); newest_selection_head.to_point(&snapshot.display_snapshot);
let buffer = snapshot let buffer = snapshot.buffer_snapshot.buffer_line_for_row(
.buffer_snapshot MultiBufferRow(newest_selection_point.row),
.buffer_line_for_row(MultiBufferRow(newest_selection_point.row)); );
if let Some((buffer, range)) = buffer { if let Some((buffer, range)) = buffer {
let buffer_id = buffer.remote_id(); let buffer_id = buffer.remote_id();
let row = range.start.row; let row = range.start.row;
@ -4120,7 +4160,8 @@ impl Element for EditorElement {
self.editor.read(cx).tasks.contains_key(&(buffer_id, row)); self.editor.read(cx).tasks.contains_key(&(buffer_id, row));
if !has_test_indicator { if !has_test_indicator {
code_actions_indicator = self.layout_code_actions_indicator( code_actions_indicator = self
.layout_code_actions_indicator(
line_height, line_height,
newest_selection_head, newest_selection_head,
scroll_pixel_position, scroll_pixel_position,
@ -4250,6 +4291,7 @@ impl Element for EditorElement {
} }
}) })
}) })
})
} }
fn paint( fn paint(
@ -4303,6 +4345,8 @@ impl Element for EditorElement {
} }
} }
}); });
let rem_size = self.rem_size(cx);
cx.with_rem_size(rem_size, |cx| {
cx.with_text_style(Some(text_style), |cx| { cx.with_text_style(Some(text_style), |cx| {
cx.with_content_mask(Some(ContentMask { bounds }), |cx| { cx.with_content_mask(Some(ContentMask { bounds }), |cx| {
self.paint_mouse_listeners(layout, hovered_hunk, cx); self.paint_mouse_listeners(layout, hovered_hunk, cx);
@ -4323,6 +4367,7 @@ impl Element for EditorElement {
self.paint_mouse_context_menu(layout, cx); self.paint_mouse_context_menu(layout, cx);
}); });
}) })
})
} }
} }

View file

@ -490,7 +490,15 @@ pub struct Window {
display_id: DisplayId, display_id: DisplayId,
sprite_atlas: Arc<dyn PlatformAtlas>, sprite_atlas: Arc<dyn PlatformAtlas>,
text_system: Arc<WindowTextSystem>, text_system: Arc<WindowTextSystem>,
pub(crate) rem_size: Pixels, rem_size: Pixels,
/// An override value for the window's rem size.
///
/// This is used by `with_rem_size` to allow rendering an element tree with
/// a given rem size.
///
/// Note: Right now we only allow for a single override value at a time, but
/// this could likely be changed to be a stack of rem sizes.
rem_size_override: Option<Pixels>,
pub(crate) viewport_size: Size<Pixels>, pub(crate) viewport_size: Size<Pixels>,
layout_engine: Option<TaffyLayoutEngine>, layout_engine: Option<TaffyLayoutEngine>,
pub(crate) root_view: Option<AnyView>, pub(crate) root_view: Option<AnyView>,
@ -763,6 +771,7 @@ impl Window {
sprite_atlas, sprite_atlas,
text_system, text_system,
rem_size: px(16.), rem_size: px(16.),
rem_size_override: None,
viewport_size: content_size, viewport_size: content_size,
layout_engine: Some(TaffyLayoutEngine::new()), layout_engine: Some(TaffyLayoutEngine::new()),
root_view: None, root_view: None,
@ -1202,7 +1211,9 @@ impl<'a> WindowContext<'a> {
/// The size of an em for the base font of the application. Adjusting this value allows the /// The size of an em for the base font of the application. Adjusting this value allows the
/// UI to scale, just like zooming a web page. /// UI to scale, just like zooming a web page.
pub fn rem_size(&self) -> Pixels { pub fn rem_size(&self) -> Pixels {
self.window.rem_size self.window
.rem_size_override
.unwrap_or(self.window.rem_size)
} }
/// Sets the size of an em for the base font of the application. Adjusting this value allows the /// Sets the size of an em for the base font of the application. Adjusting this value allows the
@ -1211,6 +1222,31 @@ impl<'a> WindowContext<'a> {
self.window.rem_size = rem_size.into(); self.window.rem_size = rem_size.into();
} }
/// Executes the provided function with the specified rem size.
///
/// This method must only be called as part of element drawing.
pub fn with_rem_size<F, R>(&mut self, rem_size: Option<impl Into<Pixels>>, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
debug_assert!(
matches!(
self.window.draw_phase,
DrawPhase::Prepaint | DrawPhase::Paint
),
"this method can only be called during request_layout, prepaint, or paint"
);
if let Some(rem_size) = rem_size {
self.window.rem_size_override = Some(rem_size.into());
let result = f(self);
self.window.rem_size_override.take();
result
} else {
f(self)
}
}
/// The line height associated with the current text style. /// The line height associated with the current text style.
pub fn line_height(&self) -> Pixels { pub fn line_height(&self) -> Pixels {
let rem_size = self.rem_size(); let rem_size = self.rem_size();