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,
};
use sum_tree::Bias;
use theme::{ActiveTheme, PlayerColor};
use theme::{ActiveTheme, PlayerColor, ThemeSettings};
use ui::prelude::*;
use ui::{h_flex, ButtonLike, ButtonStyle, ContextMenu, Tooltip};
use util::ResultExt;
@ -3705,6 +3705,38 @@ enum Invisible {
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 {
type RequestLayoutState = ();
type PrepaintState = EditorLayout;
@ -3718,6 +3750,8 @@ impl Element for EditorElement {
_: Option<&GlobalElementId>,
cx: &mut WindowContext,
) -> (gpui::LayoutId, ()) {
let rem_size = self.rem_size(cx);
cx.with_rem_size(rem_size, |cx| {
self.editor.update(cx, |editor, cx| {
editor.set_style(self.style.clone(), cx);
@ -3761,6 +3795,7 @@ impl Element for EditorElement {
(layout_id, ())
})
})
}
fn prepaint(
@ -3776,6 +3811,9 @@ impl Element for EditorElement {
..Default::default()
};
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_content_mask(Some(ContentMask { bounds }), |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| {
autoscroll_containing_element =
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);
});
@ -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()
.width;
let mut scroll_width =
@ -4110,9 +4150,9 @@ impl Element for EditorElement {
if gutter_settings.code_actions {
let newest_selection_point =
newest_selection_head.to_point(&snapshot.display_snapshot);
let buffer = snapshot
.buffer_snapshot
.buffer_line_for_row(MultiBufferRow(newest_selection_point.row));
let buffer = snapshot.buffer_snapshot.buffer_line_for_row(
MultiBufferRow(newest_selection_point.row),
);
if let Some((buffer, range)) = buffer {
let buffer_id = buffer.remote_id();
let row = range.start.row;
@ -4120,7 +4160,8 @@ impl Element for EditorElement {
self.editor.read(cx).tasks.contains_key(&(buffer_id, row));
if !has_test_indicator {
code_actions_indicator = self.layout_code_actions_indicator(
code_actions_indicator = self
.layout_code_actions_indicator(
line_height,
newest_selection_head,
scroll_pixel_position,
@ -4250,6 +4291,7 @@ impl Element for EditorElement {
}
})
})
})
}
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_content_mask(Some(ContentMask { bounds }), |cx| {
self.paint_mouse_listeners(layout, hovered_hunk, cx);
@ -4323,6 +4367,7 @@ impl Element for EditorElement {
self.paint_mouse_context_menu(layout, cx);
});
})
})
}
}

View file

@ -490,7 +490,15 @@ pub struct Window {
display_id: DisplayId,
sprite_atlas: Arc<dyn PlatformAtlas>,
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>,
layout_engine: Option<TaffyLayoutEngine>,
pub(crate) root_view: Option<AnyView>,
@ -763,6 +771,7 @@ impl Window {
sprite_atlas,
text_system,
rem_size: px(16.),
rem_size_override: None,
viewport_size: content_size,
layout_engine: Some(TaffyLayoutEngine::new()),
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
/// UI to scale, just like zooming a web page.
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
@ -1211,6 +1222,31 @@ impl<'a> WindowContext<'a> {
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.
pub fn line_height(&self) -> Pixels {
let rem_size = self.rem_size();