Merge pull request #462 from pop-os/scroll-fixes
Fix horizontal scrolling with wheel or touchpad and improve scrollbar behavior
This commit is contained in:
commit
e66aa263a0
1 changed files with 119 additions and 97 deletions
216
src/text_box.rs
216
src/text_box.rs
|
|
@ -324,7 +324,7 @@ where
|
||||||
) -> mouse::Interaction {
|
) -> mouse::Interaction {
|
||||||
let state = tree.state.downcast_ref::<State>();
|
let state = tree.state.downcast_ref::<State>();
|
||||||
|
|
||||||
if let Some(Dragging::ScrollbarV { .. }) = &state.dragging {
|
if let Some(Dragging::ScrollbarV { .. } | Dragging::ScrollbarH { .. }) = &state.dragging {
|
||||||
return mouse::Interaction::Idle;
|
return mouse::Interaction::Idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -367,12 +367,12 @@ where
|
||||||
let mut editor = self.editor.lock().unwrap();
|
let mut editor = self.editor.lock().unwrap();
|
||||||
|
|
||||||
let cosmic_theme = theme.cosmic();
|
let cosmic_theme = theme.cosmic();
|
||||||
let scrollbar_w = cosmic_theme.spacing.space_xxs as i32;
|
let scrollbar_size = cosmic_theme.spacing.space_xxs as i32;
|
||||||
|
|
||||||
let view_position = layout.position() + [self.padding.left, self.padding.top].into();
|
let view_position = layout.position() + [self.padding.left, self.padding.top].into();
|
||||||
let view_w = cmp::min(viewport.width as i32, layout.bounds().width as i32)
|
let view_w = cmp::min(viewport.width as i32, layout.bounds().width as i32)
|
||||||
- self.padding.horizontal() as i32
|
- self.padding.horizontal() as i32
|
||||||
- scrollbar_w;
|
- scrollbar_size;
|
||||||
let view_h = cmp::min(viewport.height as i32, layout.bounds().height as i32)
|
let view_h = cmp::min(viewport.height as i32, layout.bounds().height as i32)
|
||||||
- self.padding.vertical() as i32;
|
- self.padding.vertical() as i32;
|
||||||
|
|
||||||
|
|
@ -597,7 +597,7 @@ where
|
||||||
let rect = Rectangle::new(
|
let rect = Rectangle::new(
|
||||||
[image_w as f32 / scale_factor, start_y as f32 / scale_factor].into(),
|
[image_w as f32 / scale_factor, start_y as f32 / scale_factor].into(),
|
||||||
Size::new(
|
Size::new(
|
||||||
scrollbar_w as f32,
|
scrollbar_size as f32,
|
||||||
(end_y as f32 - start_y as f32) / scale_factor,
|
(end_y as f32 - start_y as f32) / scale_factor,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -606,17 +606,17 @@ where
|
||||||
let (buffer_w_opt, buffer_h_opt) = buffer.size();
|
let (buffer_w_opt, buffer_h_opt) = buffer.size();
|
||||||
let buffer_w = buffer_w_opt.unwrap_or(0.0);
|
let buffer_w = buffer_w_opt.unwrap_or(0.0);
|
||||||
let buffer_h = buffer_h_opt.unwrap_or(0.0);
|
let buffer_h = buffer_h_opt.unwrap_or(0.0);
|
||||||
let scrollbar_h_width = image_w as f32 / scale_factor - scrollbar_w as f32;
|
let scrollbar_h_width = (image_w as f32) / scale_factor;
|
||||||
if buffer_w < max_line_width {
|
if buffer_w < max_line_width {
|
||||||
let rect = Rectangle::new(
|
let rect = Rectangle::new(
|
||||||
[
|
[
|
||||||
(buffer.scroll().horizontal / max_line_width) * scrollbar_h_width,
|
(buffer.scroll().horizontal / max_line_width) * scrollbar_h_width,
|
||||||
buffer_h / scale_factor - scrollbar_w as f32,
|
buffer_h / scale_factor - scrollbar_size as f32,
|
||||||
]
|
]
|
||||||
.into(),
|
.into(),
|
||||||
Size::new(
|
Size::new(
|
||||||
(buffer_w / max_line_width) * scrollbar_h_width,
|
(buffer_w / max_line_width) * scrollbar_h_width,
|
||||||
scrollbar_w as f32,
|
scrollbar_size as f32,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
state.scrollbar_h_rect.set(Some(rect));
|
state.scrollbar_h_rect.set(Some(rect));
|
||||||
|
|
@ -643,88 +643,82 @@ where
|
||||||
// Draw editor UI
|
// Draw editor UI
|
||||||
renderer.with_translation(Vector::new(view_position.x, view_position.y), |renderer| {
|
renderer.with_translation(Vector::new(view_position.x, view_position.y), |renderer| {
|
||||||
renderer.with_transformation(Transformation::scale(1.0 / scale_factor), |renderer| {
|
renderer.with_transformation(Transformation::scale(1.0 / scale_factor), |renderer| {
|
||||||
renderer.with_layer(
|
// Draw cached image (only has line numbers)
|
||||||
Rectangle::new(
|
if let Some(ref handle) = *handle_opt {
|
||||||
Point::new(0.0, 0.0),
|
let image_size = image::Renderer::measure_image(renderer, handle);
|
||||||
Size::new(image_w as f32, image_h as f32),
|
image::Renderer::draw_image(
|
||||||
),
|
renderer,
|
||||||
|renderer| {
|
handle.clone(),
|
||||||
// Draw cached image (only has line numbers)
|
image::FilterMethod::Nearest,
|
||||||
if let Some(ref handle) = *handle_opt {
|
Rectangle::new(
|
||||||
let image_size = image::Renderer::measure_image(renderer, handle);
|
Point::new(0.0, 0.0),
|
||||||
image::Renderer::draw_image(
|
Size::new(image_size.width as f32, image_size.height as f32),
|
||||||
renderer,
|
),
|
||||||
handle.clone(),
|
Radians(0.0),
|
||||||
image::FilterMethod::Nearest,
|
1.0,
|
||||||
Rectangle::new(
|
[0.0; 4],
|
||||||
Point::new(0.0, 0.0),
|
);
|
||||||
Size::new(image_size.width as f32, image_size.height as f32),
|
}
|
||||||
),
|
|
||||||
Radians(0.0),
|
|
||||||
1.0,
|
|
||||||
[0.0; 4],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate editor position
|
// Calculate editor position
|
||||||
let scroll_x = editor.with_buffer(|buffer| buffer.scroll().horizontal);
|
let scroll_x = editor.with_buffer(|buffer| buffer.scroll().horizontal);
|
||||||
let pos = Point::new(editor_offset_x as f32, 0.0);
|
let pos = Point::new(editor_offset_x as f32 - scroll_x, 0.0);
|
||||||
let size = Size::new((image_w - editor_offset_x) as f32, image_h as f32);
|
let size = Size::new((image_w - editor_offset_x) as f32, image_h as f32);
|
||||||
|
let clip_bounds = Rectangle::new(Point::new(editor_offset_x as f32, 0.0), size);
|
||||||
|
renderer.with_layer(clip_bounds, |renderer| {
|
||||||
|
// Create custom renderer for rectangles
|
||||||
|
let mut custom_renderer = CustomRenderer { renderer, pos };
|
||||||
|
|
||||||
// Create custom renderer for rectangles
|
// Draw line highlight
|
||||||
let mut custom_renderer = CustomRenderer { renderer, pos };
|
if self.highlight_current_line {
|
||||||
|
let line_highlight = {
|
||||||
// Draw line highlight
|
let convert_color = |color: syntect::highlighting::Color| {
|
||||||
if self.highlight_current_line {
|
cosmic_text::Color::rgba(color.r, color.g, color.b, color.a)
|
||||||
let line_highlight = {
|
|
||||||
let convert_color = |color: syntect::highlighting::Color| {
|
|
||||||
cosmic_text::Color::rgba(color.r, color.g, color.b, color.a)
|
|
||||||
};
|
|
||||||
let syntax_theme = editor.theme();
|
|
||||||
//TODO: ideal fallback for line highlight color
|
|
||||||
syntax_theme
|
|
||||||
.settings
|
|
||||||
.line_highlight
|
|
||||||
.map_or(editor.background_color(), convert_color)
|
|
||||||
};
|
};
|
||||||
|
let syntax_theme = editor.theme();
|
||||||
|
//TODO: ideal fallback for line highlight color
|
||||||
|
syntax_theme
|
||||||
|
.settings
|
||||||
|
.line_highlight
|
||||||
|
.map_or(editor.background_color(), convert_color)
|
||||||
|
};
|
||||||
|
|
||||||
let cursor = editor.cursor();
|
let cursor = editor.cursor();
|
||||||
editor.with_buffer(|buffer| {
|
editor.with_buffer(|buffer| {
|
||||||
for run in buffer.layout_runs() {
|
for run in buffer.layout_runs() {
|
||||||
if run.line_i != cursor.line {
|
if run.line_i != cursor.line {
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
custom_renderer.rectangle(
|
|
||||||
0,
|
|
||||||
run.line_top as i32,
|
|
||||||
(image_w - editor_offset_x) as u32,
|
|
||||||
metrics.line_height as u32,
|
|
||||||
line_highlight,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
custom_renderer.rectangle(
|
||||||
|
0,
|
||||||
|
run.line_top as i32,
|
||||||
|
(image_w - editor_offset_x) as u32,
|
||||||
|
metrics.line_height as u32,
|
||||||
|
line_highlight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw editor selection, cursor, etc.
|
||||||
|
editor.render(&mut custom_renderer);
|
||||||
|
|
||||||
|
// Draw editor text
|
||||||
|
match editor.buffer_ref() {
|
||||||
|
cosmic_text::BufferRef::Arc(buffer) => {
|
||||||
|
renderer.fill_raw(Raw {
|
||||||
|
buffer: Arc::downgrade(&buffer),
|
||||||
|
position: pos,
|
||||||
|
color: Color::new(1.0, 1.0, 1.0, 1.0),
|
||||||
|
clip_bounds,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
// Draw editor selection, cursor, etc.
|
log::error!("cosmic-text buffer not an Arc");
|
||||||
editor.render(&mut custom_renderer);
|
|
||||||
|
|
||||||
// Draw editor text
|
|
||||||
match editor.buffer_ref() {
|
|
||||||
cosmic_text::BufferRef::Arc(buffer) => {
|
|
||||||
renderer.fill_raw(Raw {
|
|
||||||
buffer: Arc::downgrade(&buffer),
|
|
||||||
position: pos - Vector::new(scroll_x, 0.0),
|
|
||||||
color: Color::new(1.0, 1.0, 1.0, 1.0),
|
|
||||||
clip_bounds: Rectangle::new(pos, size),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
log::error!("cosmic-text buffer not an Arc");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -820,7 +814,7 @@ where
|
||||||
// Draw horizontal scrollbar
|
// Draw horizontal scrollbar
|
||||||
//TODO: reduce repitition
|
//TODO: reduce repitition
|
||||||
if let Some(scrollbar_h_rect) = state.scrollbar_h_rect.get() {
|
if let Some(scrollbar_h_rect) = state.scrollbar_h_rect.get() {
|
||||||
/*TODO: horizontal scrollbar track?
|
/*
|
||||||
// neutral_3, 0.7
|
// neutral_3, 0.7
|
||||||
let track_color = cosmic_theme
|
let track_color = cosmic_theme
|
||||||
.palette
|
.palette
|
||||||
|
|
@ -832,9 +826,12 @@ where
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
Quad {
|
Quad {
|
||||||
bounds: Rectangle::new(
|
bounds: Rectangle::new(
|
||||||
Point::new(image_position.x, image_position.y + scrollbar_h_rect.y),
|
Point::new(
|
||||||
|
image_position.x + scrollbar_h_rect.x,
|
||||||
|
image_position.y + scrollbar_h_rect.y,
|
||||||
|
),
|
||||||
Size::new(
|
Size::new(
|
||||||
layout.bounds().width - scrollbar_w as f32,
|
layout.bounds().width - scrollbar_h_rect.x - scrollbar_size as f32,
|
||||||
scrollbar_h_rect.height,
|
scrollbar_h_rect.height,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -1095,7 +1092,10 @@ where
|
||||||
// Do this first as the horizontal scrollbar is on top of the buffer
|
// Do this first as the horizontal scrollbar is on top of the buffer
|
||||||
if let Some(scrollbar_h_rect) = state.scrollbar_h_rect.get() {
|
if let Some(scrollbar_h_rect) = state.scrollbar_h_rect.get() {
|
||||||
if scrollbar_h_rect.contains(Point::new(x_logical, y_logical)) {
|
if scrollbar_h_rect.contains(Point::new(x_logical, y_logical)) {
|
||||||
state.dragging = Some(Dragging::ScrollbarH { start_x: x });
|
state.dragging = Some(Dragging::ScrollbarH {
|
||||||
|
start_x: x,
|
||||||
|
start_scroll: editor.with_buffer(|buffer| buffer.scroll()),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1232,7 +1232,10 @@ where
|
||||||
buffer.set_scroll(scroll);
|
buffer.set_scroll(scroll);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Dragging::ScrollbarH { start_x } => {
|
Dragging::ScrollbarH {
|
||||||
|
start_x,
|
||||||
|
start_scroll,
|
||||||
|
} => {
|
||||||
editor.with_buffer_mut(|buffer| {
|
editor.with_buffer_mut(|buffer| {
|
||||||
//TODO: store this in state?
|
//TODO: store this in state?
|
||||||
let mut max_line_width = 0.0;
|
let mut max_line_width = 0.0;
|
||||||
|
|
@ -1244,10 +1247,10 @@ where
|
||||||
|
|
||||||
let buffer_w = buffer.size().0.unwrap_or(0.0);
|
let buffer_w = buffer.size().0.unwrap_or(0.0);
|
||||||
let mut scroll = buffer.scroll();
|
let mut scroll = buffer.scroll();
|
||||||
scroll.horizontal = (((x - start_x) / buffer_w)
|
let scroll_offset = ((x - start_x) / buffer_w) * max_line_width;
|
||||||
* max_line_width)
|
scroll.horizontal = (start_scroll.horizontal + scroll_offset)
|
||||||
.max(0.0)
|
.min(max_line_width - buffer_w)
|
||||||
.min(max_line_width - buffer_w);
|
.max(0.0);
|
||||||
buffer.set_scroll(scroll);
|
buffer.set_scroll(scroll);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1258,15 +1261,33 @@ where
|
||||||
}
|
}
|
||||||
Event::Mouse(MouseEvent::WheelScrolled { delta }) => {
|
Event::Mouse(MouseEvent::WheelScrolled { delta }) => {
|
||||||
if let Some(_p) = cursor_position.position_in(layout.bounds()) {
|
if let Some(_p) = cursor_position.position_in(layout.bounds()) {
|
||||||
let pixels = match delta {
|
let (mut x, mut y) = match delta {
|
||||||
ScrollDelta::Lines { x: _, y } => {
|
ScrollDelta::Lines { x, y } => {
|
||||||
//TODO: this adjustment is just a guess!
|
//TODO: this adjustment is just a guess!
|
||||||
let metrics = editor.with_buffer(|buffer| buffer.metrics());
|
let metrics = editor.with_buffer(|buffer| buffer.metrics());
|
||||||
-y * metrics.line_height
|
(-x * metrics.line_height, -y * metrics.line_height)
|
||||||
}
|
}
|
||||||
ScrollDelta::Pixels { x: _, y } => -y,
|
ScrollDelta::Pixels { x, y } => (-x, -y),
|
||||||
} * 4.0;
|
};
|
||||||
editor.action(Action::Scroll { pixels });
|
x *= 4.0;
|
||||||
|
y *= 4.0;
|
||||||
|
editor.action(Action::Scroll { pixels: y });
|
||||||
|
editor.with_buffer_mut(|buffer| {
|
||||||
|
//TODO: store this in state?
|
||||||
|
let mut max_line_width = 0.0;
|
||||||
|
for run in buffer.layout_runs() {
|
||||||
|
if run.line_w > max_line_width {
|
||||||
|
max_line_width = run.line_w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer_w = buffer.size().0.unwrap_or(0.0);
|
||||||
|
let mut scroll = buffer.scroll();
|
||||||
|
scroll.horizontal = (scroll.horizontal + x)
|
||||||
|
.min(max_line_width - buffer_w)
|
||||||
|
.max(0.0);
|
||||||
|
buffer.set_scroll(scroll);
|
||||||
|
});
|
||||||
status = Status::Captured;
|
status = Status::Captured;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1302,10 +1323,11 @@ enum ClickKind {
|
||||||
Triple,
|
Triple,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
enum Dragging {
|
enum Dragging {
|
||||||
Buffer,
|
Buffer,
|
||||||
ScrollbarV { start_y: f32, start_scroll: Scroll },
|
ScrollbarV { start_y: f32, start_scroll: Scroll },
|
||||||
ScrollbarH { start_x: f32 },
|
ScrollbarH { start_x: f32, start_scroll: Scroll },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue