mirror of
https://github.com/enso-org/enso.git
synced 2025-01-03 02:54:13 +03:00
Fixed PageUp and PageDown (https://github.com/enso-org/ide/pull/340)
Original commit: 87c1628448
This commit is contained in:
parent
ad6fd9321e
commit
8ebb17d538
@ -26,13 +26,13 @@ use crate::display::shape::text::glyph::font::FontRegistry;
|
||||
use crate::display::shape::text::text_field::render::TextFieldSprites;
|
||||
use crate::display::shape::text::text_field::render::assignment::GlyphLinesAssignmentUpdate;
|
||||
use crate::display::world::World;
|
||||
use crate::system::web::text_input::KeyboardBinding;
|
||||
|
||||
use data::text::TextChange;
|
||||
use data::text::TextLocation;
|
||||
use nalgebra::Vector2;
|
||||
use nalgebra::Vector3;
|
||||
use nalgebra::Vector4;
|
||||
use crate::system::web::text_input::KeyboardBinding;
|
||||
|
||||
|
||||
|
||||
@ -148,28 +148,28 @@ shared! { TextField
|
||||
self.properties.size
|
||||
}
|
||||
|
||||
/// Scroll one page down.
|
||||
pub fn page_down(&mut self) {
|
||||
self.scroll(Vector2::new(0.0, -self.size().y))
|
||||
}
|
||||
|
||||
/// Scroll one page up.
|
||||
pub fn page_up(&mut self) {
|
||||
self.scroll(Vector2::new(0.0, self.size().y))
|
||||
}
|
||||
|
||||
/// Scroll text by given offset in pixels.
|
||||
pub fn scroll(&mut self, offset:Vector2<f32>) {
|
||||
let position_change = -Vector3::new(offset.x,offset.y,0.0);
|
||||
self.rendered.display_object.mod_position(|pos| *pos += position_change);
|
||||
let mut update = self.assignment_update();
|
||||
if offset.x != 0.0 {
|
||||
update.update_after_x_scroll(offset.x);
|
||||
let scroll_position = self.scroll_position();
|
||||
let padding_lines = 2;
|
||||
let lines = self.content.lines().len() + padding_lines;
|
||||
let text_height = self.content.line_height * lines as f32;
|
||||
let view_height = self.size().y;
|
||||
let height = (text_height - view_height).max(0.0);
|
||||
let offset_y = offset.y.min(scroll_position.y).max(scroll_position.y - height);
|
||||
let offset = Vector2::new(offset.x, offset_y);
|
||||
if offset.x != 0.0 || offset.y != 0.0 {
|
||||
let position_change = -Vector3::new(offset.x,offset.y,0.0);
|
||||
self.rendered.display_object.mod_position(|pos| *pos += position_change);
|
||||
let mut update = self.assignment_update();
|
||||
if offset.x != 0.0 {
|
||||
update.update_after_x_scroll(offset.x);
|
||||
}
|
||||
if offset.y != 0.0 {
|
||||
update.update_line_assignment();
|
||||
}
|
||||
self.rendered.update_glyphs(&mut self.content);
|
||||
}
|
||||
if offset.y != 0.0 {
|
||||
update.update_line_assignment();
|
||||
}
|
||||
self.rendered.update_glyphs(&mut self.content);
|
||||
}
|
||||
|
||||
/// Get current scroll position.
|
||||
@ -204,21 +204,51 @@ shared! { TextField
|
||||
|
||||
/// Jump active cursor to point on the screen.
|
||||
pub fn jump_cursor(&mut self, point:Vector2<f32>, selecting:bool) {
|
||||
let point_on_text = self.relative_position(point);
|
||||
let content = &mut self.content;
|
||||
let mut navigation = CursorNavigation {selecting, ..CursorNavigation::default(content)};
|
||||
let point_on_text = self.relative_position(point);
|
||||
let text_field_size = self.size();
|
||||
let content = &mut self.content;
|
||||
let mut navigation = CursorNavigation{selecting,content,text_field_size};
|
||||
self.cursors.jump_cursor(&mut navigation,point_on_text);
|
||||
self.rendered.update_cursor_sprites(&self.cursors, &mut self.content,self.focused);
|
||||
}
|
||||
|
||||
/// Processes PageUp and PageDown, scrolling the page accordingly.
|
||||
fn scroll_page(&mut self, step:Step) {
|
||||
let page_height = self.size().y;
|
||||
let scrolling = match step {
|
||||
Step::PageUp => page_height,
|
||||
Step::PageDown => -page_height,
|
||||
_ => 0.0
|
||||
};
|
||||
|
||||
self.scroll(Vector2::new(0.0,scrolling));
|
||||
}
|
||||
|
||||
/// Adjust the view to make the last cursor visible.
|
||||
fn adjust_view(&mut self) {
|
||||
let last_cursor = self.cursors.last_cursor();
|
||||
let scroll_y = self.scroll_position().y;
|
||||
let view_size = self.size();
|
||||
let current_line = last_cursor.current_line(&mut self.content);
|
||||
let current_line_pos = current_line.y_position();
|
||||
let next_line_pos = current_line_pos + current_line.height;
|
||||
let y_scrolling = (scroll_y - next_line_pos + view_size.y).min(0.0);
|
||||
let y_scrolling = (scroll_y - current_line_pos).max(y_scrolling);
|
||||
let scrolling = Vector2::new(0.0,y_scrolling);
|
||||
self.scroll(scrolling);
|
||||
}
|
||||
|
||||
/// Move all cursors by given step.
|
||||
pub fn navigate_cursors(&mut self, step:Step, selecting:bool) {
|
||||
if !selecting {
|
||||
self.clear_word_occurrences()
|
||||
}
|
||||
let content = &mut self.content;
|
||||
let mut navigation = CursorNavigation {content,selecting};
|
||||
let text_field_size = self.size();
|
||||
let content = &mut self.content;
|
||||
let mut navigation = CursorNavigation{content,selecting,text_field_size};
|
||||
self.cursors.navigate_all_cursors(&mut navigation,step);
|
||||
self.scroll_page(step);
|
||||
self.adjust_view();
|
||||
self.rendered.update_cursor_sprites(&self.cursors, &mut self.content,self.focused);
|
||||
}
|
||||
|
||||
@ -370,6 +400,7 @@ impl TextField {
|
||||
// TODO[ao] updates should be done only in one place and only once per frame
|
||||
// see https://github.com/luna/ide/issues/178
|
||||
this.assignment_update().update_after_text_edit();
|
||||
this.adjust_view();
|
||||
this.rendered.update_glyphs(&mut this.content);
|
||||
this.rendered.update_cursor_sprites(&this.cursors, &mut this.content, this.focused);
|
||||
});
|
||||
@ -385,11 +416,11 @@ impl TextField {
|
||||
/// For cursors with selection it will just remove the selected text. For the rest, it will
|
||||
/// remove all content covered by `step`.
|
||||
pub fn do_delete_operation(&self, step:Step) {
|
||||
let text_field_size = self.size();
|
||||
self.with_borrowed(|this| {
|
||||
let content = &mut this.content;
|
||||
let selecting = true;
|
||||
let mut navigation = CursorNavigation
|
||||
{selecting,..CursorNavigation::default(content)};
|
||||
let mut navigation = CursorNavigation{selecting,content,text_field_size};
|
||||
let without_selection = |c:&Cursor| !c.has_selection();
|
||||
this.cursors.navigate_cursors(&mut navigation,step,without_selection);
|
||||
});
|
||||
|
@ -8,7 +8,6 @@ use nalgebra::Vector2;
|
||||
use std::ops::Range;
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Line ===
|
||||
// ============
|
||||
@ -99,11 +98,16 @@ pub struct LineFullInfo<'a> {
|
||||
}
|
||||
|
||||
impl<'a> LineFullInfo<'a> {
|
||||
/// Get the y position of the top of the line.
|
||||
pub fn y_position(&self) -> f32 {
|
||||
self.height * self.line_id as f32
|
||||
}
|
||||
|
||||
/// Get the point where a _baseline_ of current line begins (The _baseline_ is a font specific
|
||||
/// term, for details see [freetype documentation]
|
||||
/// (https://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html#section-1)).
|
||||
pub fn baseline_start(&self) -> Vector2<f32> {
|
||||
Vector2::new(0.0, (-(self.line_id as f32) - 0.85) * self.height)
|
||||
Vector2::new(0.0, -self.y_position() - self.height * 0.85)
|
||||
}
|
||||
|
||||
/// Get x position of character with given index. The position is in _text space_.
|
||||
|
@ -133,11 +133,14 @@ impl Cursor {
|
||||
/// Home, End, Ctrl+Home, etc.)
|
||||
#[derive(Copy,Clone,Debug,Eq,Hash,PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Step {Left,LeftWord,Right,RightWord,Up,Down,LineBegin,LineEnd,DocBegin,DocEnd}
|
||||
pub enum Step
|
||||
{Left,LeftWord,Right,RightWord,PageUp,Up,PageDown,Down,LineBegin,LineEnd,DocBegin,DocEnd}
|
||||
|
||||
/// A struct for cursor navigation process.
|
||||
#[derive(Debug)]
|
||||
pub struct CursorNavigation<'a> {
|
||||
/// A snapshot of TextField's size.
|
||||
pub text_field_size: Vector2<f32>,
|
||||
/// A reference to text content. This is required to obtain the x positions of chars for proper
|
||||
/// moving cursors up and down.
|
||||
pub content: &'a mut TextFieldContent,
|
||||
@ -146,12 +149,6 @@ pub struct CursorNavigation<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CursorNavigation<'a> {
|
||||
/// Creates a new CursorNavigation with defaults.
|
||||
pub fn default(content:&'a mut TextFieldContent) -> Self {
|
||||
let selecting = default();
|
||||
Self {content,selecting}
|
||||
}
|
||||
|
||||
/// Jump cursor directly to given position.
|
||||
pub fn move_cursor_to_position(&self, cursor:&mut Cursor, to:TextLocation) {
|
||||
cursor.position = to;
|
||||
@ -220,16 +217,18 @@ impl<'a> CursorNavigation<'a> {
|
||||
|
||||
/// Get cursor position one line above the given position, such the new x coordinate of
|
||||
/// displayed cursor on the screen will be nearest the current value.
|
||||
pub fn line_up_position(&mut self, position:&TextLocation) -> Option<TextLocation> {
|
||||
let prev_line = position.line.checked_sub(1);
|
||||
prev_line.map(|line| self.near_same_x_in_another_line(position,line))
|
||||
pub fn line_up_position(&mut self, position:&TextLocation, lines:usize) -> TextLocation {
|
||||
let prev_line = position.line.checked_sub(lines);
|
||||
let prev_line = prev_line.map(|line| self.near_same_x_in_another_line(position,line));
|
||||
prev_line.unwrap_or_else(TextLocation::at_document_begin)
|
||||
}
|
||||
|
||||
/// Get cursor position one line behind the given position, such the new x coordinate of
|
||||
/// displayed cursor on the screen will be nearest the current value.
|
||||
pub fn line_down_position(&mut self, position:&TextLocation) -> Option<TextLocation> {
|
||||
let next_line = Some(position.line + 1).filter(|l| *l < self.content.lines().len());
|
||||
next_line.map(|line| self.near_same_x_in_another_line(position,line))
|
||||
pub fn line_down_position(&mut self, position:&TextLocation, lines:usize) -> TextLocation {
|
||||
let next_line = Some(position.line + lines).filter(|l| *l < self.content.lines().len());
|
||||
let next_line = next_line.map(|line| self.near_same_x_in_another_line(position,line));
|
||||
next_line.unwrap_or_else(|| self.content_end_position())
|
||||
}
|
||||
|
||||
/// Returns the next column if it exists. If it doesn't exist it attempts to return the
|
||||
@ -277,6 +276,10 @@ impl<'a> CursorNavigation<'a> {
|
||||
Self::next_valid_text_location(line, previous_line, previous_column, line_end)
|
||||
}
|
||||
|
||||
fn get_lines_from_height(&self) -> usize {
|
||||
(self.text_field_size.y / self.content.line_height) as usize
|
||||
}
|
||||
|
||||
/// New position of cursor at `position` after applying `step`.
|
||||
fn new_position(&mut self, position: TextLocation, step:Step) -> TextLocation {
|
||||
match step {
|
||||
@ -284,8 +287,10 @@ impl<'a> CursorNavigation<'a> {
|
||||
Step::RightWord => self.next_word_position(&position).unwrap_or(position),
|
||||
Step::Left => self.prev_char_position(&position).unwrap_or(position),
|
||||
Step::Right => self.next_char_position(&position).unwrap_or(position),
|
||||
Step::Up => self.line_up_position(&position).unwrap_or(position),
|
||||
Step::Down => self.line_down_position(&position).unwrap_or(position),
|
||||
Step::PageUp => self.line_up_position(&position,self.get_lines_from_height()),
|
||||
Step::PageDown => self.line_down_position(&position,self.get_lines_from_height()),
|
||||
Step::Up => self.line_up_position(&position,1),
|
||||
Step::Down => self.line_down_position(&position,1),
|
||||
Step::LineBegin => TextLocation::at_line_begin(position.line),
|
||||
Step::LineEnd => self.line_end_position(position.line),
|
||||
Step::DocBegin => TextLocation::at_document_begin(),
|
||||
@ -587,13 +592,19 @@ mod test {
|
||||
expected_positions.insert(LineEnd, vec![(0,10),(1,10),(2,9)]);
|
||||
expected_positions.insert(DocBegin, vec![(0,0)]);
|
||||
expected_positions.insert(DocEnd, vec![(2,9)]);
|
||||
expected_positions.insert(PageUp, vec![(0,0),(0,9)]);
|
||||
expected_positions.insert(PageDown, vec![(2,0),(2,9)]);
|
||||
|
||||
let mut fonts = FontRegistry::new();
|
||||
let properties = TextFieldProperties::default(&mut fonts);
|
||||
let mut content = TextFieldContent::new(text,&properties);
|
||||
let mut navigation = CursorNavigation::default(&mut content);
|
||||
let mut fonts = FontRegistry::new();
|
||||
let mut properties = TextFieldProperties::default(&mut fonts);
|
||||
let two_lines_high = properties.text_size * 2.0;
|
||||
properties.size = Vector2::new(10.0, two_lines_high);
|
||||
let content = &mut TextFieldContent::new(text,&properties);
|
||||
let text_field_size = properties.size;
|
||||
let selecting = false;
|
||||
let mut navigation = CursorNavigation{selecting,content,text_field_size};
|
||||
|
||||
for step in &[/*Left,Right,Up,*/Down,/*LineBegin,LineEnd,DocBegin,DocEnd*/] {
|
||||
for step in &[Left,Right,Up,Down,LineBegin,LineEnd,DocBegin,DocEnd,PageUp,PageDown] {
|
||||
let mut cursors = Cursors::mock(initial_cursors.clone());
|
||||
cursors.navigate_all_cursors(&mut navigation,*step);
|
||||
let expected = expected_positions.get(step).unwrap();
|
||||
@ -613,16 +624,39 @@ mod test {
|
||||
let initial_cursors = vec![initial_cursor];
|
||||
let new_position = TextLocation {line:1,column:10};
|
||||
|
||||
let mut fonts = FontRegistry::new();
|
||||
let properties = TextFieldProperties::default(&mut fonts);
|
||||
let mut content = TextFieldContent::new(text,&properties);
|
||||
let mut navigation = CursorNavigation::default(&mut content);
|
||||
let mut fonts = FontRegistry::new();
|
||||
let properties = TextFieldProperties::default(&mut fonts);
|
||||
let content = &mut TextFieldContent::new(text,&properties);
|
||||
let selecting = false;
|
||||
let text_field_size = properties.size;
|
||||
let mut navigation = CursorNavigation{content,text_field_size,selecting};
|
||||
let mut cursors = Cursors::mock(initial_cursors.clone());
|
||||
cursors.navigate_all_cursors(&mut navigation,LineEnd);
|
||||
assert_eq!(new_position, cursors.first_cursor().position);
|
||||
assert_eq!(new_position, cursors.first_cursor().selected_to);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
async fn page_scrolling() {
|
||||
ensogl_core_msdf_sys::initialized().await;
|
||||
let text = "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19";
|
||||
let initial_cursor = Cursor::new(TextLocation::at_document_begin());
|
||||
let initial_cursors = vec![initial_cursor];
|
||||
let expected_position = TextLocation {line:6,column:0};
|
||||
|
||||
let mut fonts = FontRegistry::new();
|
||||
let mut properties = TextFieldProperties::default(&mut fonts);
|
||||
properties.size = Vector2::new(100.0,100.0);
|
||||
let content = &mut TextFieldContent::new(text,&properties);
|
||||
let selecting = false;
|
||||
let text_field_size = properties.size;
|
||||
let mut navigation = CursorNavigation{selecting,content,text_field_size};
|
||||
let mut cursors = Cursors::mock(initial_cursors.clone());
|
||||
cursors.navigate_all_cursors(&mut navigation,PageDown);
|
||||
assert_eq!(expected_position, cursors.first_cursor().position);
|
||||
assert_eq!(expected_position, cursors.first_cursor().selected_to);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
async fn moving_with_select() {
|
||||
ensogl_core_msdf_sys::initialized().await;
|
||||
@ -631,11 +665,12 @@ mod test {
|
||||
let initial_cursors = vec![Cursor::new(initial_loc)];
|
||||
let new_loc = TextLocation {line:0,column:9};
|
||||
|
||||
let mut fonts = FontRegistry::new();
|
||||
let properties = TextFieldProperties::default(&mut fonts);
|
||||
let mut content = TextFieldContent::new(text,&properties);
|
||||
let mut navigation = CursorNavigation
|
||||
{selecting:true, ..CursorNavigation::default(&mut content)};
|
||||
let mut fonts = FontRegistry::new();
|
||||
let properties = TextFieldProperties::default(&mut fonts);
|
||||
let content = &mut TextFieldContent::new(text,&properties);
|
||||
let selecting = true;
|
||||
let text_field_size = properties.size;
|
||||
let mut navigation = CursorNavigation{selecting,content,text_field_size};
|
||||
let mut cursors = Cursors::mock(initial_cursors.clone());
|
||||
cursors.navigate_all_cursors(&mut navigation,LineEnd);
|
||||
assert_eq!(new_loc , cursors.first_cursor().position);
|
||||
@ -697,12 +732,14 @@ mod test {
|
||||
#[wasm_bindgen_test(async)]
|
||||
async fn step_into_word() {
|
||||
msdf_sys::initialized().await;
|
||||
let content = "first sentence\r\nthis is a second sentence\r\nlast sentence\n";
|
||||
let content = &mut TextFieldContent::new(content,&mock_properties());
|
||||
let selecting = false;
|
||||
let mut navigation = CursorNavigation{content,selecting};
|
||||
let mut location = TextLocation::at_document_begin();
|
||||
location = navigation.next_word_position(&location).unwrap();
|
||||
let content = "first sentence\r\nthis is a second sentence\r\nlast sentence\n";
|
||||
let properties = mock_properties();
|
||||
let content = &mut TextFieldContent::new(content,&properties);
|
||||
let selecting = false;
|
||||
let text_field_size = properties.size;
|
||||
let mut navigation = CursorNavigation{content,selecting,text_field_size};
|
||||
let mut location = TextLocation::at_document_begin();
|
||||
location = navigation.next_word_position(&location).unwrap();
|
||||
assert_eq!(location, TextLocation{line:0, column:5});
|
||||
location = navigation.next_word_position(&location).unwrap();
|
||||
assert_eq!(location, TextLocation{line:0, column:14});
|
||||
|
@ -150,6 +150,8 @@ impl TextFieldKeyboardFrp {
|
||||
setter.set_navigation_action(&[ArrowRight], Step::Right);
|
||||
setter.set_navigation_action(&[ArrowUp], Step::Up);
|
||||
setter.set_navigation_action(&[ArrowDown], Step::Down);
|
||||
setter.set_navigation_action(&[PageDown], Step::PageDown);
|
||||
setter.set_navigation_action(&[PageUp], Step::PageUp);
|
||||
setter.set_navigation_action(&[Home], Step::LineBegin);
|
||||
setter.set_navigation_action(&[End], Step::LineEnd);
|
||||
setter.set_navigation_action(&[Control,Home], Step::DocBegin);
|
||||
@ -161,8 +163,6 @@ impl TextFieldKeyboardFrp {
|
||||
setter.set_action(&[Delete], |t| t.do_delete_operation(Step::Right));
|
||||
setter.set_action(&[Backspace], |t| t.do_delete_operation(Step::Left));
|
||||
setter.set_action(&[Escape], |t| t.finish_multicursor_mode());
|
||||
setter.set_action(&[PageDown], |t| t.page_down());
|
||||
setter.set_action(&[PageUp], |t| t.page_up());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ impl TextEditor {
|
||||
let font = fonts.get_or_load_embedded_font("DejaVuSansMono").unwrap();
|
||||
let padding = default();
|
||||
let position = zero();
|
||||
let size = Vector2::new(screen.width, screen.height);
|
||||
let size = Vector2::new(screen.width, screen.height / 2.0);
|
||||
let black = Vector4::new(0.0,0.0,0.0,1.0);
|
||||
let base_color = black;
|
||||
let text_size = 16.0;
|
||||
|
Loading…
Reference in New Issue
Block a user