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:
parent
c8ddde27e1
commit
5b8bb6237f
2 changed files with 560 additions and 479 deletions
|
@ -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);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue