input: Fix horizontal scrolling offset. (#1191)
Continue #1131 to fix some scroll offset details. - Keep line number at left. - Show scrollbar next the line_numbers right side. - Fix scroll_size calculate issue that will always show vertical scrollbar, even not have enough contents. <img width="888" height="641" alt="image" src="https://github.com/user-attachments/assets/5d388377-3f6d-4530-b837-6c719e1eb48f" />
This commit is contained in:
parent
ca548bd856
commit
6569bead8b
5 changed files with 152 additions and 73 deletions
|
|
@ -5,7 +5,7 @@ use gpui_component::{
|
||||||
h_flex,
|
h_flex,
|
||||||
highlighter::{Language, LanguageConfig, LanguageRegistry},
|
highlighter::{Language, LanguageConfig, LanguageRegistry},
|
||||||
input::{InputEvent, InputState, Marker, TabSize, TextInput},
|
input::{InputEvent, InputState, Marker, TabSize, TextInput},
|
||||||
v_flex, ActiveTheme, ContextModal, IconName, IndexPath, Sizable,
|
v_flex, ActiveTheme, ContextModal, IconName, IndexPath, Selectable, Sizable,
|
||||||
};
|
};
|
||||||
use story::Assets;
|
use story::Assets;
|
||||||
|
|
||||||
|
|
@ -30,6 +30,7 @@ pub struct Example {
|
||||||
language: Lang,
|
language: Lang,
|
||||||
line_number: bool,
|
line_number: bool,
|
||||||
need_update: bool,
|
need_update: bool,
|
||||||
|
soft_wrap: bool,
|
||||||
_subscribes: Vec<Subscription>,
|
_subscribes: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,6 +100,7 @@ impl Example {
|
||||||
tab_size: 4,
|
tab_size: 4,
|
||||||
hard_tabs: false,
|
hard_tabs: false,
|
||||||
})
|
})
|
||||||
|
.soft_wrap(false)
|
||||||
.default_value(default_language.1)
|
.default_value(default_language.1)
|
||||||
.placeholder("Enter your code here...")
|
.placeholder("Enter your code here...")
|
||||||
});
|
});
|
||||||
|
|
@ -140,6 +142,7 @@ impl Example {
|
||||||
language: default_language.0,
|
language: default_language.0,
|
||||||
line_number: true,
|
line_number: true,
|
||||||
need_update: false,
|
need_update: false,
|
||||||
|
soft_wrap: false,
|
||||||
_subscribes,
|
_subscribes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +223,14 @@ impl Example {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_soft_wrap(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
self.soft_wrap = !self.soft_wrap;
|
||||||
|
self.editor.update(cx, |state, cx| {
|
||||||
|
state.set_soft_wrap(self.soft_wrap, window, cx);
|
||||||
|
});
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for Example {
|
impl Render for Example {
|
||||||
|
|
@ -232,7 +243,6 @@ impl Render for Example {
|
||||||
.id("source")
|
.id("source")
|
||||||
.w_full()
|
.w_full()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.gap_2()
|
|
||||||
.child(
|
.child(
|
||||||
TextInput::new(&self.editor)
|
TextInput::new(&self.editor)
|
||||||
.bordered(false)
|
.bordered(false)
|
||||||
|
|
@ -272,7 +282,15 @@ impl Render for Example {
|
||||||
});
|
});
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})),
|
})),
|
||||||
),
|
)
|
||||||
|
.child({
|
||||||
|
Button::new("soft-wrap")
|
||||||
|
.ghost()
|
||||||
|
.xsmall()
|
||||||
|
.label("Soft Wrap")
|
||||||
|
.selected(self.soft_wrap)
|
||||||
|
.on_click(cx.listener(Self::toggle_soft_wrap))
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
.child({
|
.child({
|
||||||
let loc = self.editor.read(cx).line_column();
|
let loc = self.editor.read(cx).line_column();
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@ use crate::{
|
||||||
|
|
||||||
use super::{mode::InputMode, InputState, LastLayout};
|
use super::{mode::InputMode, InputState, LastLayout};
|
||||||
|
|
||||||
const RIGHT_MARGIN: Pixels = px(5.);
|
pub(super) const RIGHT_MARGIN: Pixels = px(10.);
|
||||||
const BOTTOM_MARGIN_ROWS: usize = 1;
|
const BOTTOM_MARGIN_ROWS: usize = 1;
|
||||||
const LINE_NUMBER_MARGIN_RIGHT: Pixels = px(10.);
|
pub(super) const LINE_NUMBER_RIGHT_MARGIN: Pixels = px(10.);
|
||||||
|
|
||||||
pub(super) struct TextElement {
|
pub(super) struct TextElement {
|
||||||
state: Entity<InputState>,
|
state: Entity<InputState>,
|
||||||
|
|
@ -129,16 +129,17 @@ impl TextElement {
|
||||||
let selection_changed = state.last_selected_range != Some(selected_range);
|
let selection_changed = state.last_selected_range != Some(selected_range);
|
||||||
|
|
||||||
if cursor_moved || selection_changed {
|
if cursor_moved || selection_changed {
|
||||||
scroll_offset.x =
|
scroll_offset.x = if scroll_offset.x + cursor_pos.x
|
||||||
if scroll_offset.x + cursor_pos.x > (bounds.size.width - RIGHT_MARGIN) {
|
> (bounds.size.width - line_number_width - RIGHT_MARGIN)
|
||||||
// cursor is out of right
|
{
|
||||||
bounds.size.width - RIGHT_MARGIN - cursor_pos.x
|
// cursor is out of right
|
||||||
} else if scroll_offset.x + cursor_pos.x < px(0.) {
|
bounds.size.width - line_number_width - RIGHT_MARGIN - cursor_pos.x
|
||||||
// cursor is out of left
|
} else if scroll_offset.x + cursor_pos.x < px(0.) {
|
||||||
scroll_offset.x - cursor_pos.x
|
// cursor is out of left
|
||||||
} else {
|
scroll_offset.x - cursor_pos.x
|
||||||
scroll_offset.x
|
} else {
|
||||||
};
|
scroll_offset.x
|
||||||
|
};
|
||||||
scroll_offset.y = if scroll_offset.y + cursor_pos.y + line_height
|
scroll_offset.y = if scroll_offset.y + cursor_pos.y + line_height
|
||||||
> bounds.size.height - bottom_margin
|
> bounds.size.height - bottom_margin
|
||||||
{
|
{
|
||||||
|
|
@ -512,12 +513,13 @@ impl Element for TextElement {
|
||||||
style.size.width = relative(1.).into();
|
style.size.width = relative(1.).into();
|
||||||
if state.mode.is_multi_line() {
|
if state.mode.is_multi_line() {
|
||||||
style.flex_grow = 1.0;
|
style.flex_grow = 1.0;
|
||||||
if let Some(h) = state.mode.height() {
|
style.size.height = relative(1.).into();
|
||||||
style.size.height = h.into();
|
if state.mode.is_auto_grow() {
|
||||||
style.min_size.height = line_height.into();
|
// Auto grow to let height match to rows, but not exceed max rows.
|
||||||
|
let rows = state.mode.max_rows().min(state.mode.rows());
|
||||||
|
style.min_size.height = (rows * line_height).into();
|
||||||
} else {
|
} else {
|
||||||
style.size.height = relative(1.).into();
|
style.min_size.height = line_height.into();
|
||||||
style.min_size.height = (state.mode.rows() * line_height).into();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For single-line inputs, the minimum height should be the line height
|
// For single-line inputs, the minimum height should be the line height
|
||||||
|
|
@ -583,7 +585,7 @@ impl Element for TextElement {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let line_number_width = if state.mode.line_number() {
|
let line_number_width = if state.mode.line_number() {
|
||||||
empty_line_number.last().unwrap().width() + LINE_NUMBER_MARGIN_RIGHT
|
empty_line_number.last().unwrap().width() + LINE_NUMBER_RIGHT_MARGIN
|
||||||
} else {
|
} else {
|
||||||
px(0.)
|
px(0.)
|
||||||
};
|
};
|
||||||
|
|
@ -674,21 +676,21 @@ impl Element for TextElement {
|
||||||
.expect("failed to shape text");
|
.expect("failed to shape text");
|
||||||
// measure.end();
|
// measure.end();
|
||||||
|
|
||||||
let total_wrapped_lines = lines
|
let mut max_line_width = px(0.);
|
||||||
.iter()
|
let mut total_wrapped_lines = 0;
|
||||||
.map(|line| {
|
for line in lines.iter() {
|
||||||
// +1 is the first line, `wrap_boundaries` is the wrapped lines after the `\n`.
|
// FIXME: The `shape_text` measured width is not stable, sometime will large, sometime small.
|
||||||
1 + line.wrap_boundaries.len()
|
max_line_width = max_line_width.max(line.width());
|
||||||
})
|
// +1 is the first line, `wrap_boundaries` is the wrapped lines after the `\n`.
|
||||||
.sum::<usize>();
|
total_wrapped_lines += 1 + line.wrap_boundaries.len();
|
||||||
|
}
|
||||||
|
|
||||||
let max_line_width = lines
|
|
||||||
.iter()
|
|
||||||
.map(|line| line.width())
|
|
||||||
.max()
|
|
||||||
.unwrap_or(bounds.size.width);
|
|
||||||
let scroll_size = size(
|
let scroll_size = size(
|
||||||
max_line_width + line_number_width + RIGHT_MARGIN,
|
if max_line_width + line_number_width + RIGHT_MARGIN > bounds.size.width {
|
||||||
|
max_line_width + line_number_width + RIGHT_MARGIN
|
||||||
|
} else {
|
||||||
|
max_line_width
|
||||||
|
},
|
||||||
(total_wrapped_lines as f32 * line_height).max(bounds.size.height),
|
(total_wrapped_lines as f32 * line_height).max(bounds.size.height),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -879,6 +881,7 @@ impl Element for TextElement {
|
||||||
|
|
||||||
let active_line_color = cx.theme().highlight_theme.style.active_line;
|
let active_line_color = cx.theme().highlight_theme.style.active_line;
|
||||||
|
|
||||||
|
// Paint active line
|
||||||
let mut offset_y = px(0.);
|
let mut offset_y = px(0.);
|
||||||
if let Some(line_numbers) = prepaint.line_numbers.as_ref() {
|
if let Some(line_numbers) = prepaint.line_numbers.as_ref() {
|
||||||
offset_y += invisible_top_padding;
|
offset_y += invisible_top_padding;
|
||||||
|
|
@ -887,7 +890,7 @@ impl Element for TextElement {
|
||||||
for (ix, lines) in line_numbers.iter().enumerate() {
|
for (ix, lines) in line_numbers.iter().enumerate() {
|
||||||
let is_active = prepaint.current_line_index == Some(visible_range.start + ix);
|
let is_active = prepaint.current_line_index == Some(visible_range.start + ix);
|
||||||
for line in lines {
|
for line in lines {
|
||||||
let p = point(origin.x, origin.y + offset_y);
|
let p = point(input_bounds.origin.x, origin.y + offset_y);
|
||||||
let line_size = line.size(line_height);
|
let line_size = line.size(line_height);
|
||||||
// Paint the current line background
|
// Paint the current line background
|
||||||
if is_active {
|
if is_active {
|
||||||
|
|
@ -898,7 +901,6 @@ impl Element for TextElement {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = line.paint(p, line_height, TextAlign::Left, None, window, cx);
|
|
||||||
offset_y += line_size.height;
|
offset_y += line_size.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -913,7 +915,6 @@ impl Element for TextElement {
|
||||||
|
|
||||||
// Paint text
|
// Paint text
|
||||||
let mut offset_y = mask_offset_y + invisible_top_padding;
|
let mut offset_y = mask_offset_y + invisible_top_padding;
|
||||||
|
|
||||||
for line in prepaint
|
for line in prepaint
|
||||||
.last_layout
|
.last_layout
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -928,6 +929,7 @@ impl Element for TextElement {
|
||||||
offset_y += line.size(line_height).height;
|
offset_y += line.size(line_height).height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Paint blinking cursor
|
||||||
if focused {
|
if focused {
|
||||||
if let Some(mut cursor_bounds) = prepaint.cursor_bounds.take() {
|
if let Some(mut cursor_bounds) = prepaint.cursor_bounds.take() {
|
||||||
cursor_bounds.origin.y += prepaint.cursor_scroll_offset.y;
|
cursor_bounds.origin.y += prepaint.cursor_scroll_offset.y;
|
||||||
|
|
@ -935,6 +937,54 @@ impl Element for TextElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Paint line numbers
|
||||||
|
let mut offset_y = px(0.);
|
||||||
|
if let Some(line_numbers) = prepaint.line_numbers.as_ref() {
|
||||||
|
offset_y += invisible_top_padding;
|
||||||
|
|
||||||
|
// Paint line number background
|
||||||
|
window.paint_quad(fill(
|
||||||
|
Bounds {
|
||||||
|
origin: input_bounds.origin,
|
||||||
|
size: size(
|
||||||
|
prepaint.last_layout.line_number_width,
|
||||||
|
input_bounds.size.height,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
cx.theme()
|
||||||
|
.highlight_theme
|
||||||
|
.style
|
||||||
|
.background
|
||||||
|
.unwrap_or(cx.theme().input),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Each item is the normal lines.
|
||||||
|
for (ix, lines) in line_numbers.iter().enumerate() {
|
||||||
|
for line in lines {
|
||||||
|
let p = point(input_bounds.origin.x, origin.y + offset_y);
|
||||||
|
|
||||||
|
let is_active = prepaint.current_line_index == Some(visible_range.start + ix);
|
||||||
|
let line_size = line.size(line_height);
|
||||||
|
|
||||||
|
// paint active line number background
|
||||||
|
if is_active {
|
||||||
|
if let Some(bg_color) = active_line_color {
|
||||||
|
window.paint_quad(fill(
|
||||||
|
Bounds::new(
|
||||||
|
p,
|
||||||
|
size(prepaint.last_layout.line_number_width, line_height),
|
||||||
|
),
|
||||||
|
bg_color,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = line.paint(p, line_height, TextAlign::Left, None, window, cx);
|
||||||
|
offset_y += line_size.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.state.update(cx, |state, cx| {
|
self.state.update(cx, |state, cx| {
|
||||||
state.last_layout = Some(prepaint.last_layout.clone());
|
state.last_layout = Some(prepaint.last_layout.clone());
|
||||||
state.last_bounds = Some(bounds);
|
state.last_bounds = Some(bounds);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{cell::RefCell, ops::Range};
|
use std::{cell::RefCell, ops::Range};
|
||||||
|
|
||||||
use gpui::{App, DefiniteLength, SharedString};
|
use gpui::{App, SharedString};
|
||||||
|
|
||||||
use crate::{highlighter::SyntaxHighlighter, input::marker::Marker};
|
use crate::{highlighter::SyntaxHighlighter, input::marker::Marker};
|
||||||
|
|
||||||
|
|
@ -41,7 +41,6 @@ pub enum InputMode {
|
||||||
MultiLine {
|
MultiLine {
|
||||||
tab: TabSize,
|
tab: TabSize,
|
||||||
rows: usize,
|
rows: usize,
|
||||||
height: Option<DefiniteLength>,
|
|
||||||
},
|
},
|
||||||
AutoGrow {
|
AutoGrow {
|
||||||
rows: usize,
|
rows: usize,
|
||||||
|
|
@ -51,7 +50,6 @@ pub enum InputMode {
|
||||||
CodeEditor {
|
CodeEditor {
|
||||||
tab: TabSize,
|
tab: TabSize,
|
||||||
rows: usize,
|
rows: usize,
|
||||||
height: Option<DefiniteLength>,
|
|
||||||
/// Show line number
|
/// Show line number
|
||||||
line_number: bool,
|
line_number: bool,
|
||||||
language: SharedString,
|
language: SharedString,
|
||||||
|
|
@ -104,18 +102,6 @@ impl InputMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_height(&mut self, new_height: Option<DefiniteLength>) {
|
|
||||||
match self {
|
|
||||||
InputMode::MultiLine { height, .. } => {
|
|
||||||
*height = new_height;
|
|
||||||
}
|
|
||||||
InputMode::CodeEditor { height, .. } => {
|
|
||||||
*height = new_height;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn update_auto_grow(&mut self, text_wrapper: &TextWrapper) {
|
pub(super) fn update_auto_grow(&mut self, text_wrapper: &TextWrapper) {
|
||||||
let wrapped_lines = text_wrapper.wrapped_lines.len();
|
let wrapped_lines = text_wrapper.wrapped_lines.len();
|
||||||
self.set_rows(wrapped_lines);
|
self.set_rows(wrapped_lines);
|
||||||
|
|
@ -152,14 +138,6 @@ impl InputMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn height(&self) -> Option<DefiniteLength> {
|
|
||||||
match self {
|
|
||||||
InputMode::MultiLine { height, .. } => *height,
|
|
||||||
InputMode::CodeEditor { height, .. } => *height,
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return false if the mode is not [`InputMode::CodeEditor`].
|
/// Return false if the mode is not [`InputMode::CodeEditor`].
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ use std::rc::Rc;
|
||||||
use unicode_segmentation::*;
|
use unicode_segmentation::*;
|
||||||
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, point, prelude::FluentBuilder as _, px, relative, App, AppContext, Bounds,
|
actions, div, point, prelude::FluentBuilder as _, px, App, AppContext, Bounds, ClipboardItem,
|
||||||
ClipboardItem, Context, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable,
|
Context, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable,
|
||||||
InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton, MouseDownEvent,
|
InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton, MouseDownEvent,
|
||||||
MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render, ScrollHandle,
|
MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Render, ScrollHandle,
|
||||||
ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection, Window, WrappedLine,
|
ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection, Window, WrappedLine,
|
||||||
|
|
@ -224,7 +224,7 @@ pub(super) struct LastLayout {
|
||||||
pub(super) visible_range: Range<usize>,
|
pub(super) visible_range: Range<usize>,
|
||||||
/// The wrap width of text layout, this will change will InputElement painted.
|
/// The wrap width of text layout, this will change will InputElement painted.
|
||||||
pub(super) wrap_width: Option<Pixels>,
|
pub(super) wrap_width: Option<Pixels>,
|
||||||
/// The line number width of text layout.
|
/// The line number area width of text layout, if not line number, this will be 0px.
|
||||||
pub(super) line_number_width: Pixels,
|
pub(super) line_number_width: Pixels,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,7 +362,6 @@ impl InputState {
|
||||||
pub fn multi_line(mut self) -> Self {
|
pub fn multi_line(mut self) -> Self {
|
||||||
self.mode = InputMode::MultiLine {
|
self.mode = InputMode::MultiLine {
|
||||||
rows: 2,
|
rows: 2,
|
||||||
height: None,
|
|
||||||
tab: TabSize::default(),
|
tab: TabSize::default(),
|
||||||
};
|
};
|
||||||
self
|
self
|
||||||
|
|
@ -404,7 +403,6 @@ impl InputState {
|
||||||
language,
|
language,
|
||||||
highlighter: Rc::new(RefCell::new(None)),
|
highlighter: Rc::new(RefCell::new(None)),
|
||||||
line_number: true,
|
line_number: true,
|
||||||
height: Some(relative(1.)),
|
|
||||||
markers: Rc::new(vec![]),
|
markers: Rc::new(vec![]),
|
||||||
};
|
};
|
||||||
self
|
self
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, relative, AnyElement, App, DefiniteLength, Entity, InteractiveElement as _,
|
div, px, relative, AnyElement, App, DefiniteLength, Entity, InteractiveElement as _,
|
||||||
IntoElement, MouseButton, ParentElement as _, Rems, RenderOnce, StyleRefinement, Styled,
|
IntoElement, IsZero, MouseButton, ParentElement as _, Rems, RenderOnce, StyleRefinement,
|
||||||
Window,
|
Styled, Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::button::{Button, ButtonVariants as _};
|
use crate::button::{Button, ButtonVariants as _};
|
||||||
use crate::indicator::Indicator;
|
use crate::indicator::Indicator;
|
||||||
use crate::input::clear_button;
|
use crate::input::clear_button;
|
||||||
|
use crate::input::element::{LINE_NUMBER_RIGHT_MARGIN, RIGHT_MARGIN};
|
||||||
use crate::scroll::Scrollbar;
|
use crate::scroll::Scrollbar;
|
||||||
use crate::ActiveTheme;
|
use crate::ActiveTheme;
|
||||||
use crate::{h_flex, StyledExt};
|
use crate::{h_flex, StyledExt};
|
||||||
|
|
@ -153,7 +154,6 @@ impl RenderOnce for TextInput {
|
||||||
let font_size = window.text_style().font_size.to_pixels(window.rem_size());
|
let font_size = window.text_style().font_size.to_pixels(window.rem_size());
|
||||||
|
|
||||||
self.state.update(cx, |state, cx| {
|
self.state.update(cx, |state, cx| {
|
||||||
state.mode.set_height(self.height);
|
|
||||||
state.text_wrapper.set_font(font, font_size, cx);
|
state.text_wrapper.set_font(font, font_size, cx);
|
||||||
state.disabled = self.disabled;
|
state.disabled = self.disabled;
|
||||||
});
|
});
|
||||||
|
|
@ -299,8 +299,43 @@ impl RenderOnce for TextInput {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.refine_style(&self.style)
|
.refine_style(&self.style)
|
||||||
.when(state.mode.is_multi_line(), |this| {
|
.when(state.mode.is_multi_line(), |mut this| {
|
||||||
if state.last_layout.is_some() {
|
let paddings = this.style().padding.clone();
|
||||||
|
let base_size = window.text_style().font_size;
|
||||||
|
let rem_size = window.rem_size();
|
||||||
|
|
||||||
|
let paddings = gpui::Edges {
|
||||||
|
left: paddings
|
||||||
|
.left
|
||||||
|
.map(|v| v.to_pixels(base_size, rem_size))
|
||||||
|
.unwrap_or(px(0.)),
|
||||||
|
right: paddings
|
||||||
|
.right
|
||||||
|
.map(|v| v.to_pixels(base_size, rem_size))
|
||||||
|
.unwrap_or(px(0.)),
|
||||||
|
top: paddings
|
||||||
|
.top
|
||||||
|
.map(|v| v.to_pixels(base_size, rem_size))
|
||||||
|
.unwrap_or(px(0.)),
|
||||||
|
bottom: paddings
|
||||||
|
.bottom
|
||||||
|
.map(|v| v.to_pixels(base_size, rem_size))
|
||||||
|
.unwrap_or(px(0.)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(last_layout) = state.last_layout.as_ref() {
|
||||||
|
let left = if last_layout.line_number_width.is_zero() {
|
||||||
|
px(0.)
|
||||||
|
} else {
|
||||||
|
// Align left edge to the Line number.
|
||||||
|
paddings.left + last_layout.line_number_width - LINE_NUMBER_RIGHT_MARGIN
|
||||||
|
};
|
||||||
|
|
||||||
|
let scroll_size = gpui::Size {
|
||||||
|
width: state.scroll_size.width - left + paddings.right + RIGHT_MARGIN,
|
||||||
|
height: state.scroll_size.height,
|
||||||
|
};
|
||||||
|
|
||||||
let scrollbar = if !state.soft_wrap {
|
let scrollbar = if !state.soft_wrap {
|
||||||
Scrollbar::both(&state.scroll_state, &state.scroll_handle)
|
Scrollbar::both(&state.scroll_state, &state.scroll_handle)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -311,10 +346,10 @@ impl RenderOnce for TextInput {
|
||||||
div()
|
div()
|
||||||
.absolute()
|
.absolute()
|
||||||
.top_0()
|
.top_0()
|
||||||
.left_0()
|
.left(left)
|
||||||
.right(px(1.))
|
.right_0()
|
||||||
.bottom_0()
|
.bottom_0()
|
||||||
.child(scrollbar.scroll_size(state.scroll_size)),
|
.child(scrollbar.scroll_size(scroll_size)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue