Partially implement scrollbar

This commit is contained in:
Jeremy Soller 2023-11-10 11:20:55 -07:00
parent c66a28320a
commit ac3e7ade96
No known key found for this signature in database
GPG key ID: DCFCA852D3906975
3 changed files with 124 additions and 54 deletions

18
Cargo.lock generated
View file

@ -865,7 +865,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-text" name = "cosmic-text"
version = "0.10.0" version = "0.10.0"
source = "git+https://github.com/pop-os/cosmic-text?branch=vi-editor#9efcc41a5aca6df16a66da53aae75b1191ba0576" source = "git+https://github.com/pop-os/cosmic-text?branch=vi-editor#e8dd8ec7d1a75219980a653998552a78eb9ec0c0"
dependencies = [ dependencies = [
"fontdb 0.15.0", "fontdb 0.15.0",
"libm", "libm",
@ -874,7 +874,7 @@ dependencies = [
"rangemap", "rangemap",
"rustc-hash", "rustc-hash",
"rustybuzz 0.11.0", "rustybuzz 0.11.0",
"self_cell 1.0.1", "self_cell 1.0.2",
"swash", "swash",
"syntect", "syntect",
"sys-locale", "sys-locale",
@ -1206,9 +1206,9 @@ dependencies = [
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.10.0" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
dependencies = [ dependencies = [
"humantime", "humantime",
"is-terminal", "is-terminal",
@ -2744,7 +2744,7 @@ dependencies = [
[[package]] [[package]]
name = "modit" name = "modit"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/modit.git#0bfedf9b11412b2f3612bdc5ffebcf882f88eb01" source = "git+https://github.com/pop-os/modit.git#6e8bcf69b7c2790e70c328374811699237520ba1"
dependencies = [ dependencies = [
"log", "log",
] ]
@ -3954,9 +3954,9 @@ checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"
[[package]] [[package]]
name = "self_cell" name = "self_cell"
version = "1.0.1" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6"
[[package]] [[package]]
name = "serde" name = "serde"
@ -4081,9 +4081,9 @@ dependencies = [
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.11.1" version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]] [[package]]
name = "smithay-client-toolkit" name = "smithay-client-toolkit"

View file

@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
use cosmic::{ use cosmic::{
app::{message, Command, Core, Settings}, app::{message, Command, Core, Settings},
cosmic_config::{self, CosmicConfigEntry}, cosmic_config::{self, CosmicConfigEntry},
@ -15,9 +17,7 @@ use cosmic::{
}; };
use cosmic_text::{Cursor, Edit, Family, FontSystem, SwashCache, SyntaxSystem, ViMode}; use cosmic_text::{Cursor, Edit, Family, FontSystem, SwashCache, SyntaxSystem, ViMode};
use std::{ use std::{
env, env, fs,
fmt::Write,
fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
process, process,
sync::Mutex, sync::Mutex,
@ -927,8 +927,7 @@ impl cosmic::Application for App {
} }
} }
}; };
tab_column = tab_column = tab_column.push(text_box(&tab.editor, self.config.metrics()));
tab_column.push(text_box(&tab.editor, self.config.metrics()).padding(8));
tab_column = tab_column.push(text(status).font(cosmic::font::Font::MONOSPACE)); tab_column = tab_column.push(text(status).font(cosmic::font::Font::MONOSPACE));
} }
None => { None => {

View file

@ -5,13 +5,13 @@ use cosmic::{
event::{Event, Status}, event::{Event, Status},
keyboard::{Event as KeyEvent, KeyCode, Modifiers}, keyboard::{Event as KeyEvent, KeyCode, Modifiers},
mouse::{self, Button, Event as MouseEvent, ScrollDelta}, mouse::{self, Button, Event as MouseEvent, ScrollDelta},
Color, Element, Length, Padding, Rectangle, Size, Color, Element, Length, Padding, Point, Rectangle, Size, Vector,
}, },
iced_core::{ iced_core::{
clipboard::Clipboard, clipboard::Clipboard,
image, image,
layout::{self, Layout}, layout::{self, Layout},
renderer, renderer::{self, Quad},
widget::{self, tree, Widget}, widget::{self, tree, Widget},
Shell, Shell,
}, },
@ -195,17 +195,32 @@ where
fn mouse_interaction( fn mouse_interaction(
&self, &self,
_tree: &widget::Tree, tree: &widget::Tree,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: mouse::Cursor, cursor_position: mouse::Cursor,
_viewport: &Rectangle, _viewport: &Rectangle,
_renderer: &Renderer, _renderer: &Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
if cursor_position.is_over(layout.bounds()) { let state = tree.state.downcast_ref::<State>();
mouse::Interaction::Text
} else { match &state.dragging {
mouse::Interaction::Idle Some(Dragging::Scrollbar { .. }) => return mouse::Interaction::Idle,
_ => {}
} }
if let Some(p) = cursor_position.position_in(layout.bounds()) {
let scale_factor = state.scale_factor.get();
let editor = self.editor.lock().unwrap();
let buffer_size = editor.buffer().size();
let x = (p.x - self.padding.left) * scale_factor;
let y = (p.y - self.padding.top) * scale_factor;
if x >= 0.0 && x < buffer_size.0 && y >= 0.0 && y < buffer_size.1 {
return mouse::Interaction::Text;
}
}
mouse::Interaction::Idle
} }
fn draw( fn draw(
@ -250,10 +265,12 @@ where
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;
let scale_factor = style.scale_factor; let scale_factor = style.scale_factor as f32;
let image_w = (view_w as f64 * scale_factor) as i32; let image_w = (view_w as f32 * scale_factor) as i32;
let image_h = (view_h as f64 * scale_factor) as i32; let image_h = (view_h as f32 * scale_factor) as i32;
//TODO: make this configurable and do not repeat
let scrollbar_w = (8.0 * scale_factor) as i32; let scrollbar_w = (8.0 * scale_factor) as i32;
if image_w <= scrollbar_w || image_h <= 0 { if image_w <= scrollbar_w || image_h <= 0 {
@ -261,12 +278,15 @@ where
return; return;
} }
// Adjust image width by scrollbar width
let image_w = image_w - scrollbar_w;
let mut font_system = FONT_SYSTEM.lock().unwrap(); let mut font_system = FONT_SYSTEM.lock().unwrap();
let mut editor = editor.borrow_with(&mut font_system); let mut editor = editor.borrow_with(&mut font_system);
// Set metrics and size // Set metrics and size
editor.buffer_mut().set_metrics_and_size( editor.buffer_mut().set_metrics_and_size(
self.metrics.scale(scale_factor as f32), self.metrics.scale(scale_factor),
image_w as f32, image_w as f32,
image_h as f32, image_h as f32,
); );
@ -293,7 +313,7 @@ where
}, },
); );
// Draw scrollbar // Calculate scrollbar
{ {
let mut start_line_opt = None; let mut start_line_opt = None;
let mut end_line = 0; let mut end_line = 0;
@ -308,16 +328,15 @@ where
let lines = editor.buffer().lines.len(); let lines = editor.buffer().lines.len();
let start_y = (start_line * image_h as usize) / lines; let start_y = (start_line * image_h as usize) / lines;
let end_y = ((end_line * image_h as usize) / lines).max(start_y + 1); let end_y = ((end_line * image_h as usize) / lines).max(start_y + 1);
draw_rect(
buffer, let rect = Rectangle::new(
image_w, [image_w as f32 / scale_factor, start_y as f32 / scale_factor].into(),
image_h, Size::new(
image_w - scrollbar_w, scrollbar_w as f32 / scale_factor,
start_y as i32, (end_y as f32 - start_y as f32) / scale_factor,
scrollbar_w, ),
end_y as i32 - start_y as i32,
0x40FFFFFF,
); );
state.scrollbar_rect.set(rect);
} }
} }
@ -330,16 +349,35 @@ where
} }
let handle = state.handle.lock().unwrap().clone(); let handle = state.handle.lock().unwrap().clone();
let image_position =
layout.position() + [self.padding.left as f32, self.padding.top as f32].into();
let image_size = image::Renderer::dimensions(renderer, &handle);
image::Renderer::draw( image::Renderer::draw(
renderer, renderer,
handle, handle,
Rectangle::new( Rectangle::new(
layout.position() + [self.padding.left as f32, self.padding.top as f32].into(), image_position,
Size::new(view_w as f32, view_h as f32), Size::new(image_size.width as f32, image_size.height as f32),
), ),
[0.0; 4], [0.0; 4],
); );
// Draw scrollbar
let scrollbar_alpha = match &state.dragging {
Some(Dragging::Scrollbar { .. }) => 0.5,
_ => 0.25,
};
renderer.fill_quad(
Quad {
bounds: state.scrollbar_rect.get()
+ Vector::new(image_position.x, image_position.y),
border_radius: 0.0.into(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
Color::new(1.0, 1.0, 1.0, scrollbar_alpha),
);
let duration = instant.elapsed(); let duration = instant.elapsed();
log::debug!("redraw {}, {}: {:?}", view_w, view_h, duration); log::debug!("redraw {}, {}: {:?}", view_w, view_h, duration);
} }
@ -356,8 +394,10 @@ where
_viewport: &Rectangle<f32>, _viewport: &Rectangle<f32>,
) -> Status { ) -> Status {
let state = tree.state.downcast_mut::<State>(); let state = tree.state.downcast_mut::<State>();
let scale_factor = state.scale_factor.get() as f32; let scale_factor = state.scale_factor.get();
let scrollbar_rect = state.scrollbar_rect.get();
let mut editor = self.editor.lock().unwrap(); let mut editor = self.editor.lock().unwrap();
let buffer_size = editor.buffer().size();
let mut font_system = FONT_SYSTEM.lock().unwrap(); let mut font_system = FONT_SYSTEM.lock().unwrap();
let mut editor = editor.borrow_with(&mut font_system); let mut editor = editor.borrow_with(&mut font_system);
@ -439,27 +479,51 @@ where
} }
Event::Mouse(MouseEvent::ButtonPressed(Button::Left)) => { Event::Mouse(MouseEvent::ButtonPressed(Button::Left)) => {
if let Some(p) = cursor_position.position_in(layout.bounds()) { if let Some(p) = cursor_position.position_in(layout.bounds()) {
editor.action(Action::Click { let x = (p.x - self.padding.left) * scale_factor;
x: ((p.x - self.padding.left) * scale_factor) as i32, let y = (p.y - self.padding.top) * scale_factor;
y: ((p.y - self.padding.top) * scale_factor) as i32, if x >= 0.0 && x < buffer_size.0 && y >= 0.0 && y < buffer_size.1 {
}); editor.action(Action::Click {
state.is_dragging = true; x: x as i32,
y: y as i32,
});
state.dragging = Some(Dragging::Buffer);
} else if scrollbar_rect.contains(Point::new(x, y)) {
state.dragging = Some(Dragging::Scrollbar {
start_y: y,
start_scroll: editor.buffer().scroll(),
});
}
//TODO: support clicking below or above scrollbar
status = Status::Captured; status = Status::Captured;
} }
} }
Event::Mouse(MouseEvent::ButtonReleased(Button::Left)) => { Event::Mouse(MouseEvent::ButtonReleased(Button::Left)) => {
state.is_dragging = false; state.dragging = None;
status = Status::Captured; status = Status::Captured;
} }
Event::Mouse(MouseEvent::CursorMoved { .. }) => { Event::Mouse(MouseEvent::CursorMoved { .. }) => {
if state.is_dragging { if let Some(dragging) = &state.dragging {
if let Some(p) = cursor_position.position() { if let Some(p) = cursor_position.position() {
editor.action(Action::Drag { let x = ((p.x - layout.bounds().x) - self.padding.left) * scale_factor;
x: (((p.x - layout.bounds().x) - self.padding.left) * scale_factor) let y = ((p.y - layout.bounds().y) - self.padding.top) * scale_factor;
as i32, match dragging {
y: (((p.y - layout.bounds().y) - self.padding.top) * scale_factor) Dragging::Buffer => {
as i32, editor.action(Action::Drag {
}); x: x as i32,
y: y as i32,
});
}
Dragging::Scrollbar {
start_y,
start_scroll,
} => {
let mut buffer = editor.buffer_mut();
let scroll_offset = (((y - start_y) / buffer.size().1)
* buffer.lines.len() as f32)
as i32;
buffer.set_scroll(start_scroll + scroll_offset);
}
}
} }
status = Status::Captured; status = Status::Captured;
} }
@ -490,10 +554,16 @@ where
} }
} }
enum Dragging {
Buffer,
Scrollbar { start_y: f32, start_scroll: i32 },
}
pub struct State { pub struct State {
modifiers: Modifiers, modifiers: Modifiers,
is_dragging: bool, dragging: Option<Dragging>,
scale_factor: Cell<f64>, scale_factor: Cell<f32>,
scrollbar_rect: Cell<Rectangle<f32>>,
handle: Mutex<image::Handle>, handle: Mutex<image::Handle>,
} }
@ -502,8 +572,9 @@ impl State {
pub fn new() -> State { pub fn new() -> State {
State { State {
modifiers: Modifiers::empty(), modifiers: Modifiers::empty(),
is_dragging: false, dragging: None,
scale_factor: Cell::new(1.0), scale_factor: Cell::new(1.0),
scrollbar_rect: Cell::new(Rectangle::default()),
//TODO: make option! //TODO: make option!
handle: Mutex::new(image::Handle::from_pixels(1, 1, vec![0, 0, 0, 0])), handle: Mutex::new(image::Handle::from_pixels(1, 1, vec![0, 0, 0, 0])),
} }