make more InputState properties private

This commit is contained in:
temportalflux
2026-07-11 09:31:07 -04:00
parent 6ce4ebbb2a
commit 344804c232
3 changed files with 77 additions and 64 deletions
+12 -11
View File
@@ -191,7 +191,7 @@ struct InputStateSnapshot {
marked_range: Option<Range<usize>>,
cursor_position: usize,
logical_lines: Vec<InputLogicalLine>,
scroll_offset: Pixels,
scroll_distance: Pixels,
line_height: Pixels,
}
impl InputStateSnapshot {
@@ -201,7 +201,7 @@ impl InputStateSnapshot {
let marked_range = input_state.marked_range().cloned();
let cursor_position = input_state.cursor_position();
let logical_lines = input_state.logical_lines.clone();
let scroll_offset = input_state.scroll_offset;
let scroll_distance = input_state.distance_from_top();
let line_height = input_state.line_height();
let layout_axis = input_state.layout_style().axis();
let should_center_placeholder = matches!(
@@ -216,7 +216,7 @@ impl InputStateSnapshot {
marked_range,
cursor_position,
logical_lines,
scroll_offset,
scroll_distance,
line_height,
}
}
@@ -241,6 +241,7 @@ impl<'app> PaintContext<'app> {
) {
let axis = self.snapshot.layout_axis;
let bounds = self.bounds;
let scroll_distance = self.snapshot.scroll_distance;
window.on_mouse_event({
let input = entity.clone();
move |event: &MouseDownEvent, phase, window, cx| {
@@ -257,7 +258,7 @@ impl<'app> PaintContext<'app> {
input.update(cx, |input, cx| {
// Converts a screen position to a position relative to the text area origin, adjusted for scroll offset.
let text_position = (event.position - bounds.origin)
.apply_along(axis, |pos| pos + input.scroll_offset);
.apply_along(axis, |pos| pos + scroll_distance);
input.on_mouse_down(
text_position,
event.click_count,
@@ -293,7 +294,7 @@ impl<'app> PaintContext<'app> {
input.update(cx, |input, cx| {
// Converts a screen position to a position relative to the text area origin, adjusted for scroll offset.
let text_position = (event.position - bounds.origin)
.apply_along(axis, |pos| pos + input.scroll_offset);
.apply_along(axis, |pos| pos + scroll_distance);
input.on_mouse_move(text_position, cx);
});
}
@@ -330,7 +331,7 @@ impl<'app> PaintContext<'app> {
}
}
};
input.scroll_offset = (input.scroll_offset - delta).clamp(px(0.), max_scroll);
input.apply_scroll_delta(delta, max_scroll);
cx.notify();
});
}
@@ -372,7 +373,7 @@ impl<'app> PaintContext<'app> {
fn paint_selection(&self, window: &mut Window) {
let one_line = self.snapshot.logical_lines.len() == 1;
for line in &self.snapshot.logical_lines {
let line_y = line.y_offset - self.snapshot.scroll_offset;
let line_y = line.y_offset - self.snapshot.scroll_distance;
if !one_line {
if !self.is_line_visible(line) {
@@ -442,7 +443,7 @@ impl<'app> PaintContext<'app> {
fn paint_text(&self, window: &mut Window, cx: &mut App) {
for line_layout in &self.snapshot.logical_lines {
let line_y = line_layout.y_offset - self.snapshot.scroll_offset;
let line_y = line_layout.y_offset - self.snapshot.scroll_distance;
if !self.is_line_visible(line_layout) {
continue;
@@ -499,7 +500,7 @@ impl<'app> PaintContext<'app> {
fn find_cursor_position_in_layouts(&self) -> Point<Pixels> {
for line in &self.snapshot.logical_lines {
let line_y = line.y_offset - self.snapshot.scroll_offset;
let line_y = line.y_offset - self.snapshot.scroll_distance;
if !self.is_line_visible(line) {
continue;
@@ -544,7 +545,7 @@ impl<'app> PaintContext<'app> {
}
fn is_line_visible(&self, line: &InputLogicalLine) -> bool {
let line_y = line.y_offset - self.snapshot.scroll_offset;
let line_y = line.y_offset - self.snapshot.scroll_distance;
let line_bottom = line_y + self.snapshot.line_height * line.visual_line_count as f32;
line_bottom >= px(0.) && line_y <= self.bounds.size.height
}
@@ -565,7 +566,7 @@ impl<'app> PaintContext<'app> {
return;
};
let line_y = line.y_offset - self.snapshot.scroll_offset;
let line_y = line.y_offset - self.snapshot.scroll_distance;
let line_start = line.text_range.start;
let line_end = line.text_range.end;
+36 -23
View File
@@ -44,15 +44,15 @@ pub struct InputState {
cached_utf16_len: Option<usize>,
/// The style of layout (single or multiline).
pub(super) layout_style: InputLayoutStyle,
layout_style: InputLayoutStyle,
/// The utf-8 character range that is currently selected by the user.
/// NOTE: because each input has its own selection state, its trivial for users to have multiple selections active across multiple inputs at the same time.
/// This could be considered undesirable behavior, and doing so would prompt the question of should there be a mechanism to clear selection when focus is lost.
pub(super) selected_range: Range<usize>,
selected_range: Range<usize>,
/// The direction of the selection_range. Forward means providing in iteration order along `content`. Back means reverse iteration order.
pub(super) selection_direction: NavigationDirection,
selection_direction: NavigationDirection,
/// The utf-8 character range of `content` that is currently marked/highlighted.
pub(super) marked_range: Option<Range<usize>>,
marked_range: Option<Range<usize>>,
// refreshed each update by the element, for conveinent access in mutations and painting
pub(super) layout_data: InputLayoutData,
@@ -68,7 +68,7 @@ pub struct InputState {
/// The number of times the user has clicked `last_click_position`. Used to determine which click behavior to trigger, depending on single, double, or triple clicks.
click_count: usize,
/// The distance in pixels from the start of the text a user has scrolled along the layout_style axis (singleline is horizontal, multiline is vertical).
pub(super) scroll_offset: Pixels,
scroll_distance: Pixels,
/// The maximum duration between changes to `content` that can be grouped together as a single entry in the history log.
history_grouping_interval: Duration,
@@ -163,7 +163,7 @@ impl InputState {
is_selecting: false,
last_click_position: None,
click_count: 0,
scroll_offset: px(0.),
scroll_distance: px(0.),
history_grouping_interval: super::DEFAULT_GROUP_INTERVAL,
history_undo_stack: Vec::new(),
@@ -232,6 +232,10 @@ impl InputState {
&self.selected_range
}
pub fn selection_direction(&self) -> NavigationDirection {
self.selection_direction
}
/// Sets the selection range directly.
pub fn set_selected_range(&mut self, range: Range<usize>) {
let range = range.start.min(self.content.len())..range.end.min(self.content.len());
@@ -254,7 +258,7 @@ impl InputState {
/// Returns true if the scroll position is at the top.
pub fn at_top(&self) -> bool {
self.scroll_offset <= px(0.)
self.scroll_distance <= px(0.)
}
/// Returns true if the scroll position is at the bottom.
@@ -266,7 +270,7 @@ impl InputState {
return true;
}
self.scroll_offset + visible_height >= content_height
self.scroll_distance + visible_height >= content_height
}
/// Returns the scroll progress as a value from 0.0 (top) to 1.0 (bottom).
@@ -279,12 +283,12 @@ impl InputState {
return 0.0;
}
(self.scroll_offset / max_scroll).clamp(0.0, 1.0)
(self.scroll_distance / max_scroll).clamp(0.0, 1.0)
}
/// Returns how far the content is scrolled from the top in pixels.
pub fn distance_from_top(&self) -> Pixels {
self.scroll_offset.max(px(0.))
self.scroll_distance.max(px(0.))
}
/// Returns how far the content is from the bottom in pixels.
@@ -297,7 +301,7 @@ impl InputState {
return px(0.);
}
(max_scroll - self.scroll_offset).max(px(0.))
(max_scroll - self.scroll_distance).max(px(0.))
}
/// Configures how long the input will wait between user-input changes to create new logs in the history for undo/redo.
@@ -347,7 +351,7 @@ impl InputState {
self.cached_utf16_len = Some(cached_len - removed_utf16_len + added_utf16_len);
}
self.replace_range(range.clone(), &text_to_insert);
self.replace_text_at_range(range.clone(), &text_to_insert);
self.selected_range =
range.start + text_to_insert.len()..range.start + text_to_insert.len();
@@ -834,8 +838,12 @@ impl InputState {
self.layout_data.line_height
}
pub(super) fn set_marked_range(&mut self, range: Option<Range<usize>>) {
self.marked_range = range;
}
/// Replaces the provided utf-8 character range with the provided text
pub(super) fn replace_range(&mut self, range: Range<usize>, text: &str) {
pub(super) fn replace_text_at_range(&mut self, range: Range<usize>, text: &str) {
self.content.replace_range(range, &text);
}
@@ -944,6 +952,10 @@ impl InputState {
self.content.len()
}
pub(super) fn apply_scroll_delta(&mut self, delta: Pixels, max: Pixels) {
self.scroll_distance = (self.scroll_distance - delta).clamp(px(0.), max);
}
pub(super) fn scroll_to_cursor(&mut self) {
if self.logical_lines.is_empty() {
return;
@@ -971,19 +983,20 @@ impl InputState {
px(0.)
};
let visible_left = self.scroll_offset;
let visible_right = self.scroll_offset + self.layout_data.available_size.width;
let visible_left = self.scroll_distance;
let visible_right = self.scroll_distance + self.layout_data.available_size.width;
// Add some padding so cursor isn't right at the edge
let padding = px(2.0);
if cursor_x < visible_left + padding {
self.scroll_offset = (cursor_x - padding).max(px(0.));
self.scroll_distance = (cursor_x - padding).max(px(0.));
} else if cursor_x > visible_right - padding {
self.scroll_offset = cursor_x - self.layout_data.available_size.width + padding;
self.scroll_distance =
cursor_x - self.layout_data.available_size.width + padding;
}
self.scroll_offset = self.scroll_offset.max(px(0.));
self.scroll_distance = self.scroll_distance.max(px(0.));
}
InputLayoutStyle::MultiLine => {
if self.layout_data.available_size.height <= px(0.) {
@@ -1015,18 +1028,18 @@ impl InputState {
line.y_offset
};
let visible_top = self.scroll_offset;
let visible_top = self.scroll_distance;
let visible_bottom =
self.scroll_offset + self.layout_data.available_size.height;
self.scroll_distance + self.layout_data.available_size.height;
if cursor_visual_y < visible_top {
self.scroll_offset = cursor_visual_y;
self.scroll_distance = cursor_visual_y;
} else if cursor_visual_y + line_height > visible_bottom {
self.scroll_offset = (cursor_visual_y + line_height)
self.scroll_distance = (cursor_visual_y + line_height)
- self.layout_data.available_size.height;
}
self.scroll_offset = self.scroll_offset.max(px(0.));
self.scroll_distance = self.scroll_distance.max(px(0.));
break;
}
}
@@ -28,8 +28,8 @@ impl EntityInputHandler for super::InputState {
_cx: &mut Context<Self>,
) -> Option<UTF16Selection> {
Some(UTF16Selection {
range: self.utf_range_8to16(&self.selected_range),
reversed: self.selection_direction == NavigationDirection::Back,
range: self.utf_range_8to16(self.selected_range()),
reversed: self.selection_direction() == NavigationDirection::Back,
})
}
@@ -38,13 +38,13 @@ impl EntityInputHandler for super::InputState {
_window: &mut Window,
_cx: &mut Context<Self>,
) -> Option<Range<usize>> {
self.marked_range
self.marked_range()
.as_ref()
.map(|range| self.utf_range_8to16(range))
}
fn unmark_text(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {
self.marked_range = None;
self.set_marked_range(None);
}
fn replace_text_in_range(
@@ -57,21 +57,21 @@ impl EntityInputHandler for super::InputState {
let range = range_utf16
.as_ref()
.map(|range_utf16| self.utf_range_16to8(range_utf16))
.or(self.marked_range.clone())
.unwrap_or(self.selected_range.clone());
.or(self.marked_range().cloned())
.unwrap_or(self.selected_range().clone());
let range = range.start.min(self.content().len())..range.end.min(self.content().len());
let text_to_insert = self.layout_style.sanitize_content(new_text);
let text_to_insert = self.layout_style().sanitize_content(new_text);
// Record patch for undo before modifying content
self.push_undo_patch(range.clone(), text_to_insert.len());
self.push_undo_patch(range.clone(), text_to_insert.len());
self.update_utf16_len(range.clone(), &text_to_insert);
self.replace_range(range.clone(), &text_to_insert);
self.selected_range =
range.start + text_to_insert.len()..range.start + text_to_insert.len();
self.marked_range.take();
self.update_utf16_len(range.clone(), &text_to_insert);
self.replace_text_at_range(range.clone(), &text_to_insert);
self.set_selected_range(
range.start + text_to_insert.len()..range.start + text_to_insert.len(),
);
self.set_marked_range(None);
self.layout_data.dirty = true;
self.pause_cursor_blink(cx);
@@ -90,28 +90,27 @@ impl EntityInputHandler for super::InputState {
let range = range_utf16
.as_ref()
.map(|range_utf16| self.utf_range_16to8(range_utf16))
.or(self.marked_range.clone())
.unwrap_or(self.selected_range.clone());
.or(self.marked_range().cloned())
.unwrap_or(self.selected_range().clone());
let range = range.start.min(self.content().len())..range.end.min(self.content().len());
let text_to_insert = self.layout_style.sanitize_content(new_text);
let text_to_insert = self.layout_style().sanitize_content(new_text);
self.update_utf16_len(range.clone(), &text_to_insert);
self.replace_range(range.clone(), &text_to_insert);
if !text_to_insert.is_empty() {
self.marked_range = Some(range.start..range.start + text_to_insert.len());
} else {
self.marked_range = None;
}
self.selected_range = new_selected_range_utf16
.as_ref()
.map(|range_utf16| self.utf_range_16to8(range_utf16))
.map(|new_range| new_range.start + range.start..new_range.end + range.start)
.unwrap_or_else(|| {
self.replace_text_at_range(range.clone(), &text_to_insert);
self.set_marked_range(match text_to_insert.is_empty() {
true => None,
false => Some(range.start..range.start + text_to_insert.len()),
});
self.set_selected_range({
let new_range = new_selected_range_utf16.as_ref();
let new_range = new_range.map(|range_utf16| self.utf_range_16to8(range_utf16));
let new_range = new_range
.map(|new_range| new_range.start + range.start..new_range.end + range.start);
new_range.unwrap_or_else(|| {
range.start + text_to_insert.len()..range.start + text_to_insert.len()
});
})
});
self.layout_data.dirty = true;
cx.emit(InputStateEvent::TextChanged);