Make vertical scroll by pixels instead of layout lines
This commit is contained in:
parent
6eb67bb524
commit
56812a8348
5 changed files with 89 additions and 65 deletions
|
|
@ -148,16 +148,16 @@ fn main() {
|
||||||
if state == ElementState::Pressed {
|
if state == ElementState::Pressed {
|
||||||
match logical_key {
|
match logical_key {
|
||||||
Key::Named(NamedKey::ArrowDown) => {
|
Key::Named(NamedKey::ArrowDown) => {
|
||||||
scroll.layout += 1;
|
scroll.vertical += buffer.metrics().line_height;
|
||||||
}
|
}
|
||||||
Key::Named(NamedKey::ArrowUp) => {
|
Key::Named(NamedKey::ArrowUp) => {
|
||||||
scroll.layout -= 1;
|
scroll.vertical -= buffer.metrics().line_height;
|
||||||
}
|
}
|
||||||
Key::Named(NamedKey::PageDown) => {
|
Key::Named(NamedKey::PageDown) => {
|
||||||
scroll.layout += buffer.visible_lines();
|
scroll.vertical += buffer.size().1;
|
||||||
}
|
}
|
||||||
Key::Named(NamedKey::PageUp) => {
|
Key::Named(NamedKey::PageUp) => {
|
||||||
scroll.layout -= buffer.visible_lines();
|
scroll.vertical -= buffer.size().1;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
111
src/buffer.rs
111
src/buffer.rs
|
|
@ -94,7 +94,7 @@ pub struct LayoutRunIter<'b> {
|
||||||
buffer: &'b Buffer,
|
buffer: &'b Buffer,
|
||||||
line_i: usize,
|
line_i: usize,
|
||||||
layout_i: usize,
|
layout_i: usize,
|
||||||
total_layout: i32,
|
total_height: f32,
|
||||||
line_top: f32,
|
line_top: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ impl<'b> LayoutRunIter<'b> {
|
||||||
buffer,
|
buffer,
|
||||||
line_i: buffer.scroll.line,
|
line_i: buffer.scroll.line,
|
||||||
layout_i: 0,
|
layout_i: 0,
|
||||||
total_layout: 0,
|
total_height: 0.0,
|
||||||
line_top: 0.0,
|
line_top: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -120,25 +120,22 @@ impl<'b> Iterator for LayoutRunIter<'b> {
|
||||||
while let Some(layout_line) = layout.get(self.layout_i) {
|
while let Some(layout_line) = layout.get(self.layout_i) {
|
||||||
self.layout_i += 1;
|
self.layout_i += 1;
|
||||||
|
|
||||||
let scrolled = self.total_layout < self.buffer.scroll.layout;
|
|
||||||
self.total_layout += 1;
|
|
||||||
if scrolled {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let line_height = layout_line
|
let line_height = layout_line
|
||||||
.line_height_opt
|
.line_height_opt
|
||||||
.unwrap_or(self.buffer.metrics.line_height);
|
.unwrap_or(self.buffer.metrics.line_height);
|
||||||
let line_top = self.line_top;
|
self.total_height += line_height;
|
||||||
|
|
||||||
|
let line_top = self.line_top - self.buffer.scroll.vertical;
|
||||||
let glyph_height = layout_line.max_ascent + layout_line.max_descent;
|
let glyph_height = layout_line.max_ascent + layout_line.max_descent;
|
||||||
let centering_offset = (line_height - glyph_height) / 2.0;
|
let centering_offset = (line_height - glyph_height) / 2.0;
|
||||||
let line_y = line_top + centering_offset + layout_line.max_ascent;
|
let line_y = line_top + centering_offset + layout_line.max_ascent;
|
||||||
|
|
||||||
if line_top + centering_offset > self.buffer.height {
|
if line_top + centering_offset > self.buffer.height {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.line_top += line_height;
|
self.line_top += line_height;
|
||||||
|
if line_y < 0.0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
return Some(LayoutRun {
|
return Some(LayoutRun {
|
||||||
line_i: self.line_i,
|
line_i: self.line_i,
|
||||||
|
|
@ -314,36 +311,50 @@ impl Buffer {
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
prune: bool,
|
prune: bool,
|
||||||
) {
|
) {
|
||||||
|
let height = self.height;
|
||||||
|
let metrics = self.metrics;
|
||||||
let old_scroll = self.scroll;
|
let old_scroll = self.scroll;
|
||||||
|
|
||||||
let layout_cursor = self
|
let layout_cursor = self
|
||||||
.layout_cursor(font_system, cursor)
|
.layout_cursor(font_system, cursor)
|
||||||
.expect("shape_until_cursor invalid cursor");
|
.expect("shape_until_cursor invalid cursor");
|
||||||
|
|
||||||
|
let mut layout_y = 0.0;
|
||||||
|
let mut total_height = {
|
||||||
|
let layout = self
|
||||||
|
.line_layout(font_system, layout_cursor.line)
|
||||||
|
.expect("shape_until_cursor failed to scroll forwards");
|
||||||
|
for layout_i in 0..layout_cursor.layout {
|
||||||
|
layout_y += layout[layout_i]
|
||||||
|
.line_height_opt
|
||||||
|
.unwrap_or(metrics.line_height);
|
||||||
|
}
|
||||||
|
layout_y
|
||||||
|
+ layout[layout_cursor.layout]
|
||||||
|
.line_height_opt
|
||||||
|
.unwrap_or(metrics.line_height)
|
||||||
|
};
|
||||||
|
|
||||||
if self.scroll.line > layout_cursor.line
|
if self.scroll.line > layout_cursor.line
|
||||||
|| (self.scroll.line == layout_cursor.line
|
|| (self.scroll.line == layout_cursor.line && self.scroll.vertical > layout_y)
|
||||||
&& self.scroll.layout > layout_cursor.layout as i32)
|
|
||||||
{
|
{
|
||||||
// Adjust scroll backwards if cursor is before it
|
// Adjust scroll backwards if cursor is before it
|
||||||
self.scroll.line = layout_cursor.line;
|
self.scroll.line = layout_cursor.line;
|
||||||
self.scroll.layout = layout_cursor.layout as i32;
|
self.scroll.vertical = layout_y;
|
||||||
} else {
|
} else {
|
||||||
// Adjust scroll forwards if cursor is after it
|
// Adjust scroll forwards if cursor is after it
|
||||||
let visible_lines = self.visible_lines();
|
|
||||||
let mut line_i = layout_cursor.line;
|
let mut line_i = layout_cursor.line;
|
||||||
let mut total_layout = layout_cursor.layout as i32 + 1;
|
|
||||||
while line_i > self.scroll.line {
|
while line_i > self.scroll.line {
|
||||||
line_i -= 1;
|
line_i -= 1;
|
||||||
let layout = self
|
let layout = self
|
||||||
.line_layout(font_system, line_i)
|
.line_layout(font_system, line_i)
|
||||||
.expect("shape_until_cursor failed to scroll forwards");
|
.expect("shape_until_cursor failed to scroll forwards");
|
||||||
for layout_i in (0..layout.len()).rev() {
|
for layout_line in layout.iter() {
|
||||||
total_layout += 1;
|
total_height += layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
||||||
if total_layout >= visible_lines {
|
}
|
||||||
self.scroll.line = line_i;
|
if total_height > height + self.scroll.vertical {
|
||||||
self.scroll.layout = layout_i as i32;
|
self.scroll.line = line_i;
|
||||||
break;
|
self.scroll.vertical = total_height - height;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -385,27 +396,33 @@ impl Buffer {
|
||||||
|
|
||||||
/// Shape lines until scroll
|
/// Shape lines until scroll
|
||||||
pub fn shape_until_scroll(&mut self, font_system: &mut FontSystem, prune: bool) {
|
pub fn shape_until_scroll(&mut self, font_system: &mut FontSystem, prune: bool) {
|
||||||
|
let metrics = self.metrics;
|
||||||
let old_scroll = self.scroll;
|
let old_scroll = self.scroll;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Adjust scroll.layout to be positive by moving scroll.line backwards
|
// Adjust scroll.layout to be positive by moving scroll.line backwards
|
||||||
while self.scroll.layout < 0 {
|
while self.scroll.vertical < 0.0 {
|
||||||
if self.scroll.line > 0 {
|
if self.scroll.line > 0 {
|
||||||
self.scroll.line -= 1;
|
let line_i = self.scroll.line - 1;
|
||||||
if let Some(layout) = self.line_layout(font_system, self.scroll.line) {
|
if let Some(layout) = self.line_layout(font_system, line_i) {
|
||||||
self.scroll.layout += layout.len() as i32;
|
let mut layout_height = 0.0;
|
||||||
|
for layout_line in layout.iter() {
|
||||||
|
layout_height +=
|
||||||
|
layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
||||||
|
}
|
||||||
|
self.scroll.line = line_i;
|
||||||
|
self.scroll.vertical += layout_height;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.scroll.layout = 0;
|
self.scroll.vertical = 0.0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let visible_lines = self.visible_lines();
|
let scroll_start = self.scroll.vertical;
|
||||||
let scroll_start = self.scroll.layout;
|
let scroll_end = scroll_start + self.height;
|
||||||
let scroll_end = scroll_start + visible_lines;
|
|
||||||
|
|
||||||
let mut total_layout = 0;
|
let mut total_height = 0.0;
|
||||||
for line_i in 0..self.lines.len() {
|
for line_i in 0..self.lines.len() {
|
||||||
if line_i < self.scroll.line {
|
if line_i < self.scroll.line {
|
||||||
if prune {
|
if prune {
|
||||||
|
|
@ -413,7 +430,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if total_layout >= scroll_end {
|
if total_height > scroll_end {
|
||||||
if prune {
|
if prune {
|
||||||
self.lines[line_i].reset_shaping();
|
self.lines[line_i].reset_shaping();
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -422,22 +439,27 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut layout_height = 0.0;
|
||||||
let layout = self
|
let layout = self
|
||||||
.line_layout(font_system, line_i)
|
.line_layout(font_system, line_i)
|
||||||
.expect("shape_until_scroll invalid line");
|
.expect("shape_until_scroll invalid line");
|
||||||
for layout_i in 0..layout.len() {
|
for layout_line in layout.iter() {
|
||||||
if total_layout == scroll_start {
|
let line_height = layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
||||||
// Adjust scroll.line and scroll.layout
|
layout_height += line_height;
|
||||||
self.scroll.line = line_i;
|
total_height += line_height;
|
||||||
self.scroll.layout = layout_i as i32;
|
}
|
||||||
}
|
|
||||||
total_layout += 1;
|
// Adjust scroll.vertical to be smaller by moving scroll.line forwards
|
||||||
|
//TODO: do we want to adjust it exactly to a layout line?
|
||||||
|
if line_i == self.scroll.line && layout_height < self.scroll.vertical {
|
||||||
|
self.scroll.line += 1;
|
||||||
|
self.scroll.vertical -= layout_height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if total_layout < scroll_end && self.scroll.line > 0 {
|
if total_height < scroll_end && self.scroll.line > 0 {
|
||||||
// Need to scroll up to stay inside of buffer
|
// Need to scroll up to stay inside of buffer
|
||||||
self.scroll.layout -= scroll_end - total_layout;
|
self.scroll.vertical -= scroll_end - total_height;
|
||||||
} else {
|
} else {
|
||||||
// Done adjusting scroll
|
// Done adjusting scroll
|
||||||
break;
|
break;
|
||||||
|
|
@ -602,11 +624,6 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of lines that can be viewed in the buffer
|
|
||||||
pub fn visible_lines(&self) -> i32 {
|
|
||||||
(self.height / self.metrics.line_height) as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set text of buffer, using provided attributes for each line by default
|
/// Set text of buffer, using provided attributes for each line by default
|
||||||
pub fn set_text(
|
pub fn set_text(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
||||||
|
|
@ -137,19 +137,19 @@ pub struct Scroll {
|
||||||
/// Index of [`BufferLine`] in [`Buffer::lines`]. This will be adjusted as needed if layout is
|
/// Index of [`BufferLine`] in [`Buffer::lines`]. This will be adjusted as needed if layout is
|
||||||
/// out of bounds
|
/// out of bounds
|
||||||
pub line: usize,
|
pub line: usize,
|
||||||
/// Index of [`LayoutLine`] in [`BufferLine::layout`]. This will be adjusted as needed
|
/// Pixel offset from the start of the [`BufferLine`]. This will be adjusted as needed
|
||||||
/// if it is negative or exceeds the number of layout lines
|
/// if it is negative or exceeds the height of the [`BufferLine::layout`] lines.
|
||||||
pub layout: i32,
|
pub vertical: f32,
|
||||||
/// The horizontal position of scroll in fractional pixels
|
/// The horizontal position of scroll in fractional pixels
|
||||||
pub horizontal: f32,
|
pub horizontal: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scroll {
|
impl Scroll {
|
||||||
/// Create a new scroll
|
/// Create a new scroll
|
||||||
pub const fn new(line: usize, layout: i32, horizontal: f32) -> Self {
|
pub const fn new(line: usize, vertical: f32, horizontal: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
line,
|
line,
|
||||||
layout,
|
vertical,
|
||||||
horizontal,
|
horizontal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -851,7 +851,8 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
Action::Scroll { lines } => {
|
Action::Scroll { lines } => {
|
||||||
self.with_buffer_mut(|buffer| {
|
self.with_buffer_mut(|buffer| {
|
||||||
let mut scroll = buffer.scroll();
|
let mut scroll = buffer.scroll();
|
||||||
scroll.layout += lines;
|
//TODO: align to layout lines
|
||||||
|
scroll.vertical += lines as f32 * buffer.metrics().line_height;
|
||||||
buffer.set_scroll(scroll);
|
buffer.set_scroll(scroll);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -260,25 +260,28 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for SyntaxEditor<'syntax_system, 'bu
|
||||||
|
|
||||||
let cursor = self.cursor();
|
let cursor = self.cursor();
|
||||||
self.editor.with_buffer_mut(|buffer| {
|
self.editor.with_buffer_mut(|buffer| {
|
||||||
let visible_lines = buffer.visible_lines();
|
let metrics = buffer.metrics();
|
||||||
let scroll = buffer.scroll();
|
let scroll = buffer.scroll();
|
||||||
let scroll_end = scroll.layout + visible_lines;
|
let scroll_end = scroll.vertical + buffer.size().1;
|
||||||
let mut total_layout = 0;
|
let mut total_height = 0.0;
|
||||||
let mut highlighted = 0;
|
let mut highlighted = 0;
|
||||||
for line_i in 0..buffer.lines.len() {
|
for line_i in 0..buffer.lines.len() {
|
||||||
// Break out if we have reached the end of scroll and are past the cursor
|
// Break out if we have reached the end of scroll and are past the cursor
|
||||||
if total_layout >= scroll_end && line_i > cursor.line {
|
if total_height > scroll_end && line_i > cursor.line {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let line = &mut buffer.lines[line_i];
|
let line = &mut buffer.lines[line_i];
|
||||||
if line.metadata().is_some() && line_i < self.syntax_cache.len() {
|
if line.metadata().is_some() && line_i < self.syntax_cache.len() {
|
||||||
//TODO: duplicated code!
|
//TODO: duplicated code!
|
||||||
if line_i >= scroll.line && total_layout < scroll_end {
|
if line_i >= scroll.line && total_height < scroll_end {
|
||||||
// Perform shaping and layout of this line in order to count if we have reached scroll
|
// Perform shaping and layout of this line in order to count if we have reached scroll
|
||||||
match buffer.line_layout(font_system, line_i) {
|
match buffer.line_layout(font_system, line_i) {
|
||||||
Some(layout_lines) => {
|
Some(layout_lines) => {
|
||||||
total_layout += layout_lines.len() as i32;
|
for layout_line in layout_lines.iter() {
|
||||||
|
total_height +=
|
||||||
|
layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
//TODO: should this be possible?
|
//TODO: should this be possible?
|
||||||
|
|
@ -336,10 +339,13 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for SyntaxEditor<'syntax_system, 'bu
|
||||||
line.set_attrs_list(attrs_list);
|
line.set_attrs_list(attrs_list);
|
||||||
|
|
||||||
// Perform shaping and layout of this line in order to count if we have reached scroll
|
// Perform shaping and layout of this line in order to count if we have reached scroll
|
||||||
if line_i >= scroll.line && total_layout < scroll_end {
|
if line_i >= scroll.line && total_height < scroll_end {
|
||||||
match buffer.line_layout(font_system, line_i) {
|
match buffer.line_layout(font_system, line_i) {
|
||||||
Some(layout_lines) => {
|
Some(layout_lines) => {
|
||||||
total_layout += layout_lines.len() as i32;
|
for layout_line in layout_lines.iter() {
|
||||||
|
total_height +=
|
||||||
|
layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
//TODO: should this be possible?
|
//TODO: should this be possible?
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue