input: Keep more empty space at top or bottom of the Editor. (#1256)

<img width="727" height="586" alt="image"
src="https://github.com/user-attachments/assets/2eb27326-831c-40fc-b0c8-48e6ab6ac67f"
/>

- Fix to keep 3 lines on top when move vertical.
- Improve to keep 1 line edge for cursor, when editor is too small.
This commit is contained in:
Jason Lee 2025-09-17 10:45:56 +08:00 committed by GitHub
parent 35568124bf
commit 478db2785a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,9 +2,9 @@ use std::{ops::Range, rc::Rc};
use gpui::{
fill, point, px, relative, size, App, Bounds, Corners, Element, ElementId, ElementInputHandler,
Entity, GlobalElementId, HighlightStyle, IntoElement, LayoutId, MouseButton, MouseMoveEvent,
Path, Pixels, Point, SharedString, Size, Style, TextAlign, TextRun, UnderlineStyle, Window,
WrappedLine,
Entity, GlobalElementId, Half, HighlightStyle, IntoElement, LayoutId, MouseButton,
MouseMoveEvent, Path, Pixels, Point, SharedString, Size, Style, TextAlign, TextRun,
UnderlineStyle, Window, WrappedLine,
};
use rope::Rope;
use smallvec::SmallVec;
@ -16,8 +16,8 @@ use crate::{
use super::{mode::InputMode, InputState, LastLayout};
const BOTTOM_MARGIN_ROWS: usize = 3;
pub(super) const RIGHT_MARGIN: Pixels = px(10.);
const BOTTOM_MARGIN_ROWS: usize = 1;
pub(super) const LINE_NUMBER_RIGHT_MARGIN: Pixels = px(10.);
pub(super) struct TextElement {
@ -86,11 +86,14 @@ impl TextElement {
let mut cursor_bounds = None;
// If the input has a fixed height (Otherwise is auto-grow), we need to add a bottom margin to the input.
let bottom_margin = if state.mode.is_auto_grow() {
px(0.) + line_height
let top_bottom_margin = if state.mode.is_auto_grow() {
line_height
} else if visible_range.len() < BOTTOM_MARGIN_ROWS * 8 {
line_height
} else {
BOTTOM_MARGIN_ROWS * line_height + line_height
BOTTOM_MARGIN_ROWS * line_height
};
// The cursor corresponds to the current cursor position in the text no only the line.
let mut cursor_pos = None;
let mut cursor_start = None;
@ -176,17 +179,19 @@ impl TextElement {
} else {
scroll_offset.x
};
scroll_offset.y = if scroll_offset.y + cursor_pos.y + line_height
> bounds.size.height - bottom_margin
{
// cursor is out of bottom
bounds.size.height - bottom_margin - cursor_pos.y
} else if scroll_offset.y + cursor_pos.y < px(0.) {
// cursor is out of top
scroll_offset.y - cursor_pos.y
} else {
scroll_offset.y
};
// If we change the scroll_offset.y, GPUI will render and trigger the next run loop.
// So, here we just adjust offset by `line_height` for move smooth.
scroll_offset.y =
if scroll_offset.y + cursor_pos.y > bounds.size.height - top_bottom_margin {
// cursor is out of bottom
scroll_offset.y - line_height
} else if scroll_offset.y + cursor_pos.y < top_bottom_margin {
// cursor is out of top
(scroll_offset.y + line_height).min(px(0.))
} else {
scroll_offset.y
};
if state.selection_reversed {
if scroll_offset.x + cursor_start.x < px(0.) {
@ -727,14 +732,19 @@ impl Element for TextElement {
}
let total_wrapped_lines = state.text_wrapper.len();
let empty_bottom_height = bounds
.size
.height
.half()
.max(BOTTOM_MARGIN_ROWS * line_height);
let scroll_size = size(
if longest_line_width + line_number_width + RIGHT_MARGIN > bounds.size.width {
longest_line_width + line_number_width + RIGHT_MARGIN
} else {
longest_line_width
},
(total_wrapped_lines as f32 * line_height).max(bounds.size.height),
(total_wrapped_lines as f32 * line_height + empty_bottom_height)
.max(bounds.size.height),
);
let mut last_layout = LastLayout {