input: Fix wrong font being used in Input. (#1706)
Closes #1665 ## Description When `Input::new().font_family("font")` is used, the font hasn't been applied yet in `Input::render`, therefore the `text_wrapper` will calculate using the wrong font. This is why it's working when the font is added to the parent element. The fix moves the state update to `element` prepaint, where `window.text_style()` has the correct styles. The only downside is that there are now one additional state update. ## Screenshot | Before | After | | ---------------------------- | --------------------------- | | <img width="1125" height="246" alt="Screenshot From 2025-11-30 20-03-11" src="https://github.com/user-attachments/assets/1fa798a7-0b9c-4386-8940-ad5f6734b8a0" /> | <img width="1125" height="246" alt="Screenshot From 2025-11-30 20-00-23" src="https://github.com/user-attachments/assets/e970e7bc-1bfa-4a55-a342-3381d1684526" /> | ## How to Test Use some wide font, like `IBM Plex Mono`. Apply it to `Input::new(&some_state).font_family("IBM Plex Mono")` where the input state has `soft_wrap`. Here is a diff for `textarea_story.rs`. ```diff diff --git a/crates/story/src/textarea_story.rs b/crates/story/src/textarea_story.rs index f2c63d44..bd5cbb31 100644 --- a/crates/story/src/textarea_story.rs +++ b/crates/story/src/textarea_story.rs @@ -77,9 +77,9 @@ impl TextareaStory { let textarea_no_wrap = cx.new(|cx| { InputState::new(window, cx) - .multi_line(true) .rows(6) - .soft_wrap(false) + .multi_line(true) + .soft_wrap(true) .default_value("This is a very long line of text to test if the horizontal scrolling function is working properly, and it should not wrap automatically but display a horizontal scrollbar.\nThe second line is also very long text, used to test the horizontal scrolling effect under multiple lines, and you can input more content to test.\nThe third line: Here you can input other long text content that requires horizontal scrolling.\n") }); @@ -195,7 +185,7 @@ impl Render for TextareaStory { .child( section("Auto Grow") .max_w_md() - .child(Input::new(&self.textarea_auto_grow)), + .child(Input::new(&self.textarea_auto_grow).font_family("IBM Plex Mono")), ) .child( section("Auto Grow with No Wrap") ``` ## Checklist - [x] I have read the [CONTRIBUTING](../CONTRIBUTING.md) document and followed the guidelines. - [x] Reviewed the changes in this PR and confirmed AI generated code (If any) is accurate. - [x] Passed `cargo run` for story tests related to the changes. - [ ] Tested macOS, Windows and Linux platforms performance (if the change is platform-specific) --------- Co-authored-by: Jason Lee <huacnlee@gmail.com>
This commit is contained in:
parent
0f655314c9
commit
0126f1b036
6 changed files with 28 additions and 31 deletions
|
|
@ -85,8 +85,9 @@ impl InputStory {
|
||||||
fraction: Some(3),
|
fraction: Some(3),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let custom_input =
|
let custom_input = cx.new(|cx| {
|
||||||
cx.new(|cx| InputState::new(window, cx).placeholder("here is a custom input"));
|
InputState::new(window, cx).placeholder("Custom Input use monospace, 0123456789.")
|
||||||
|
});
|
||||||
|
|
||||||
let code_input = cx.new(|cx| {
|
let code_input = cx.new(|cx| {
|
||||||
InputState::new(window, cx)
|
InputState::new(window, cx)
|
||||||
|
|
@ -247,11 +248,12 @@ impl Render for InputStory {
|
||||||
))),
|
))),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
section("Appearance false").max_w_md().child(
|
section("Custom Appearance").max_w_md().child(
|
||||||
div()
|
div()
|
||||||
.border_b_2()
|
.border_b_2()
|
||||||
.px_6()
|
.px_6()
|
||||||
.py_3()
|
.py_3()
|
||||||
|
.font_family(cx.theme().mono_font_family.clone())
|
||||||
.border_color(cx.theme().border)
|
.border_color(cx.theme().border)
|
||||||
.bg(cx.theme().secondary)
|
.bg(cx.theme().secondary)
|
||||||
.text_color(cx.theme().secondary_foreground)
|
.text_color(cx.theme().secondary_foreground)
|
||||||
|
|
|
||||||
|
|
@ -818,6 +818,15 @@ impl Element for TextElement {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Self::PrepaintState {
|
) -> Self::PrepaintState {
|
||||||
|
let style = window.text_style();
|
||||||
|
let font = style.font();
|
||||||
|
let text_size = style.font_size.to_pixels(window.rem_size());
|
||||||
|
|
||||||
|
self.state.update(cx, |state, cx| {
|
||||||
|
state.text_wrapper.set_font(font, text_size, cx);
|
||||||
|
state.text_wrapper.prepare_if_need(&state.text, cx);
|
||||||
|
});
|
||||||
|
|
||||||
let state = self.state.read(cx);
|
let state = self.state.read(cx);
|
||||||
let line_height = window.line_height();
|
let line_height = window.line_height();
|
||||||
|
|
||||||
|
|
@ -840,8 +849,7 @@ impl Element for TextElement {
|
||||||
let text = state.text.clone();
|
let text = state.text.clone();
|
||||||
let is_empty = text.len() == 0;
|
let is_empty = text.len() == 0;
|
||||||
let placeholder = self.placeholder.clone();
|
let placeholder = self.placeholder.clone();
|
||||||
let style = window.text_style();
|
|
||||||
let font_size = style.font_size.to_pixels(window.rem_size());
|
|
||||||
let mut bounds = bounds;
|
let mut bounds = bounds;
|
||||||
|
|
||||||
let (display_text, text_color) = if is_empty {
|
let (display_text, text_color) = if is_empty {
|
||||||
|
|
@ -862,7 +870,7 @@ impl Element for TextElement {
|
||||||
|
|
||||||
// Calculate the width of the line numbers
|
// Calculate the width of the line numbers
|
||||||
let (line_number_width, line_number_len) =
|
let (line_number_width, line_number_len) =
|
||||||
Self::layout_line_numbers(&state, &text, font_size, &text_style, window);
|
Self::layout_line_numbers(&state, &text, text_size, &text_style, window);
|
||||||
|
|
||||||
let wrap_width = if multi_line && state.soft_wrap {
|
let wrap_width = if multi_line && state.soft_wrap {
|
||||||
Some(bounds.size.width - line_number_width - RIGHT_MARGIN)
|
Some(bounds.size.width - line_number_width - RIGHT_MARGIN)
|
||||||
|
|
@ -956,7 +964,7 @@ impl Element for TextElement {
|
||||||
&state,
|
&state,
|
||||||
&display_text,
|
&display_text,
|
||||||
&last_layout,
|
&last_layout,
|
||||||
font_size,
|
text_size,
|
||||||
&runs,
|
&runs,
|
||||||
&document_colors,
|
&document_colors,
|
||||||
window,
|
window,
|
||||||
|
|
@ -972,7 +980,7 @@ impl Element for TextElement {
|
||||||
.text_system()
|
.text_system()
|
||||||
.shape_line(
|
.shape_line(
|
||||||
longest_line.clone(),
|
longest_line.clone(),
|
||||||
font_size,
|
text_size,
|
||||||
&[TextRun {
|
&[TextRun {
|
||||||
len: longest_line.len(),
|
len: longest_line.len(),
|
||||||
font: style.font(),
|
font: style.font(),
|
||||||
|
|
@ -1084,7 +1092,7 @@ impl Element for TextElement {
|
||||||
sub_lines.push(
|
sub_lines.push(
|
||||||
window
|
window
|
||||||
.text_system()
|
.text_system()
|
||||||
.shape_line(line_no, font_size, &runs, None),
|
.shape_line(line_no, text_size, &runs, None),
|
||||||
);
|
);
|
||||||
for _ in 0..line.wrapped_lines.len().saturating_sub(1) {
|
for _ in 0..line.wrapped_lines.len().saturating_sub(1) {
|
||||||
sub_lines.push(ShapedLine::default());
|
sub_lines.push(ShapedLine::default());
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity, InteractiveElement as _,
|
AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity, InteractiveElement as _,
|
||||||
IntoElement, IsZero, MouseButton, ParentElement as _, Rems, RenderOnce, StyleRefinement,
|
IntoElement, IsZero, MouseButton, ParentElement as _, Rems, RenderOnce, StyleRefinement,
|
||||||
Styled, Window, div, px, relative, rems,
|
Styled, Window, div, px, relative,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::button::{Button, ButtonVariants as _};
|
use crate::button::{Button, ButtonVariants as _};
|
||||||
|
|
@ -241,16 +241,8 @@ impl Styled for Input {
|
||||||
impl RenderOnce for Input {
|
impl RenderOnce for Input {
|
||||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
const LINE_HEIGHT: Rems = Rems(1.25);
|
const LINE_HEIGHT: Rems = Rems(1.25);
|
||||||
let font = window.text_style().font();
|
|
||||||
let font_size = match self.size {
|
|
||||||
Size::Large => rems(1.),
|
|
||||||
_ => rems(0.875),
|
|
||||||
}
|
|
||||||
.to_pixels(window.rem_size());
|
|
||||||
|
|
||||||
self.state.update(cx, |state, cx| {
|
self.state.update(cx, |state, _| {
|
||||||
state.text_wrapper.set_font(font, font_size, cx);
|
|
||||||
state.text_wrapper.prepare_if_need(&state.text, cx);
|
|
||||||
state.disabled = self.disabled;
|
state.disabled = self.disabled;
|
||||||
state.size = self.size;
|
state.size = self.size;
|
||||||
});
|
});
|
||||||
|
|
@ -365,8 +357,8 @@ impl RenderOnce for Input {
|
||||||
.input_px(self.size)
|
.input_px(self.size)
|
||||||
.input_py(self.size)
|
.input_py(self.size)
|
||||||
.input_h(self.size)
|
.input_h(self.size)
|
||||||
|
.input_text_size(self.size)
|
||||||
.cursor_text()
|
.cursor_text()
|
||||||
.text_size(font_size)
|
|
||||||
.items_center()
|
.items_center()
|
||||||
.when(state.mode.is_multi_line(), |this| {
|
.when(state.mode.is_multi_line(), |this| {
|
||||||
this.h_auto()
|
this.h_auto()
|
||||||
|
|
|
||||||
|
|
@ -368,11 +368,7 @@ impl InputState {
|
||||||
Self {
|
Self {
|
||||||
focus_handle: focus_handle.clone(),
|
focus_handle: focus_handle.clone(),
|
||||||
text: "".into(),
|
text: "".into(),
|
||||||
text_wrapper: TextWrapper::new(
|
text_wrapper: TextWrapper::new(text_style.font(), window.rem_size(), None),
|
||||||
text_style.font(),
|
|
||||||
text_style.font_size.to_pixels(window.rem_size()),
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
blink_cursor,
|
blink_cursor,
|
||||||
history,
|
history,
|
||||||
selected_range: Selection::default(),
|
selected_range: Selection::default(),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use gpui::{point, px, size, App, Font, LineFragment, Pixels, Point, ShapedLine, Size, Window};
|
use gpui::{App, Font, LineFragment, Pixels, Point, ShapedLine, Size, Window, point, px, size};
|
||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
|
@ -500,7 +500,7 @@ impl LineLayout {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use gpui::{px, Boundary, FontFeatures, FontStyle, FontWeight};
|
use gpui::{Boundary, FontFeatures, FontStyle, FontWeight, px};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_update() {
|
fn test_update() {
|
||||||
|
|
|
||||||
|
|
@ -437,9 +437,9 @@ impl<T: Styled> StyleSized<T> for T {
|
||||||
match size {
|
match size {
|
||||||
Size::XSmall => self.text_xs(),
|
Size::XSmall => self.text_xs(),
|
||||||
Size::Small => self.text_sm(),
|
Size::Small => self.text_sm(),
|
||||||
Size::Medium => self.text_base(),
|
Size::Medium => self.text_sm(),
|
||||||
Size::Large => self.text_lg(),
|
Size::Large => self.text_base(),
|
||||||
Size::Size(size) => self.text_size(size),
|
Size::Size(size) => self.text_size(size * 0.875),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,7 +477,6 @@ impl<T: Styled> StyleSized<T> for T {
|
||||||
Size::XSmall => self.h_5(),
|
Size::XSmall => self.h_5(),
|
||||||
_ => self.h_6(),
|
_ => self.h_6(),
|
||||||
}
|
}
|
||||||
.input_text_size(size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue