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::{ use gpui::{
fill, point, px, relative, size, App, Bounds, Corners, Element, ElementId, ElementInputHandler, fill, point, px, relative, size, App, Bounds, Corners, Element, ElementId, ElementInputHandler,
Entity, GlobalElementId, HighlightStyle, IntoElement, LayoutId, MouseButton, MouseMoveEvent, Entity, GlobalElementId, Half, HighlightStyle, IntoElement, LayoutId, MouseButton,
Path, Pixels, Point, SharedString, Size, Style, TextAlign, TextRun, UnderlineStyle, Window, MouseMoveEvent, Path, Pixels, Point, SharedString, Size, Style, TextAlign, TextRun,
WrappedLine, UnderlineStyle, Window, WrappedLine,
}; };
use rope::Rope; use rope::Rope;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -16,8 +16,8 @@ use crate::{
use super::{mode::InputMode, InputState, LastLayout}; use super::{mode::InputMode, InputState, LastLayout};
const BOTTOM_MARGIN_ROWS: usize = 3;
pub(super) const RIGHT_MARGIN: Pixels = px(10.); 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) const LINE_NUMBER_RIGHT_MARGIN: Pixels = px(10.);
pub(super) struct TextElement { pub(super) struct TextElement {
@ -86,11 +86,14 @@ impl TextElement {
let mut cursor_bounds = None; 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. // 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() { let top_bottom_margin = if state.mode.is_auto_grow() {
px(0.) + line_height line_height
} else if visible_range.len() < BOTTOM_MARGIN_ROWS * 8 {
line_height
} else { } 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. // The cursor corresponds to the current cursor position in the text no only the line.
let mut cursor_pos = None; let mut cursor_pos = None;
let mut cursor_start = None; let mut cursor_start = None;
@ -176,17 +179,19 @@ impl TextElement {
} else { } else {
scroll_offset.x scroll_offset.x
}; };
scroll_offset.y = if scroll_offset.y + cursor_pos.y + line_height
> bounds.size.height - bottom_margin // 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.
// cursor is out of bottom scroll_offset.y =
bounds.size.height - bottom_margin - cursor_pos.y if scroll_offset.y + cursor_pos.y > bounds.size.height - top_bottom_margin {
} else if scroll_offset.y + cursor_pos.y < px(0.) { // cursor is out of bottom
// cursor is out of top scroll_offset.y - line_height
scroll_offset.y - cursor_pos.y } else if scroll_offset.y + cursor_pos.y < top_bottom_margin {
} else { // cursor is out of top
scroll_offset.y (scroll_offset.y + line_height).min(px(0.))
}; } else {
scroll_offset.y
};
if state.selection_reversed { if state.selection_reversed {
if scroll_offset.x + cursor_start.x < px(0.) { 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 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( let scroll_size = size(
if longest_line_width + line_number_width + RIGHT_MARGIN > bounds.size.width { if longest_line_width + line_number_width + RIGHT_MARGIN > bounds.size.width {
longest_line_width + line_number_width + RIGHT_MARGIN longest_line_width + line_number_width + RIGHT_MARGIN
} else { } else {
longest_line_width 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 { let mut last_layout = LastLayout {