From ac3e7ade964d2024b5489d4364d5bdf428e045c6 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 10 Nov 2023 11:20:55 -0700 Subject: [PATCH] Partially implement scrollbar --- Cargo.lock | 18 +++--- src/main.rs | 9 ++- src/text_box.rs | 151 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 124 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37d59db..478c686 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -865,7 +865,7 @@ dependencies = [ [[package]] name = "cosmic-text" 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 = [ "fontdb 0.15.0", "libm", @@ -874,7 +874,7 @@ dependencies = [ "rangemap", "rustc-hash", "rustybuzz 0.11.0", - "self_cell 1.0.1", + "self_cell 1.0.2", "swash", "syntect", "sys-locale", @@ -1206,9 +1206,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ "humantime", "is-terminal", @@ -2744,7 +2744,7 @@ dependencies = [ [[package]] name = "modit" 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 = [ "log", ] @@ -3954,9 +3954,9 @@ checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" [[package]] name = "self_cell" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" +checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" [[package]] name = "serde" @@ -4081,9 +4081,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "smithay-client-toolkit" diff --git a/src/main.rs b/src/main.rs index 6f9f5fb..b83a07f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only +//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + use cosmic::{ app::{message, Command, Core, Settings}, cosmic_config::{self, CosmicConfigEntry}, @@ -15,9 +17,7 @@ use cosmic::{ }; use cosmic_text::{Cursor, Edit, Family, FontSystem, SwashCache, SyntaxSystem, ViMode}; use std::{ - env, - fmt::Write, - fs, + env, fs, path::{Path, PathBuf}, process, sync::Mutex, @@ -927,8 +927,7 @@ impl cosmic::Application for App { } } }; - tab_column = - tab_column.push(text_box(&tab.editor, self.config.metrics()).padding(8)); + tab_column = tab_column.push(text_box(&tab.editor, self.config.metrics())); tab_column = tab_column.push(text(status).font(cosmic::font::Font::MONOSPACE)); } None => { diff --git a/src/text_box.rs b/src/text_box.rs index 4f478a1..2492df9 100644 --- a/src/text_box.rs +++ b/src/text_box.rs @@ -5,13 +5,13 @@ use cosmic::{ event::{Event, Status}, keyboard::{Event as KeyEvent, KeyCode, Modifiers}, mouse::{self, Button, Event as MouseEvent, ScrollDelta}, - Color, Element, Length, Padding, Rectangle, Size, + Color, Element, Length, Padding, Point, Rectangle, Size, Vector, }, iced_core::{ clipboard::Clipboard, image, layout::{self, Layout}, - renderer, + renderer::{self, Quad}, widget::{self, tree, Widget}, Shell, }, @@ -195,17 +195,32 @@ where fn mouse_interaction( &self, - _tree: &widget::Tree, + tree: &widget::Tree, layout: Layout<'_>, cursor_position: mouse::Cursor, _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { - if cursor_position.is_over(layout.bounds()) { - mouse::Interaction::Text - } else { - mouse::Interaction::Idle + let state = tree.state.downcast_ref::(); + + match &state.dragging { + 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( @@ -250,10 +265,12 @@ where let view_h = cmp::min(viewport.height as i32, layout.bounds().height 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_h = (view_h as f64 * scale_factor) as i32; + let image_w = (view_w as f32 * 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; if image_w <= scrollbar_w || image_h <= 0 { @@ -261,12 +278,15 @@ where return; } + // Adjust image width by scrollbar width + let image_w = image_w - scrollbar_w; + let mut font_system = FONT_SYSTEM.lock().unwrap(); let mut editor = editor.borrow_with(&mut font_system); // 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_h as f32, ); @@ -293,7 +313,7 @@ where }, ); - // Draw scrollbar + // Calculate scrollbar { let mut start_line_opt = None; let mut end_line = 0; @@ -308,16 +328,15 @@ where let lines = editor.buffer().lines.len(); let start_y = (start_line * image_h as usize) / lines; let end_y = ((end_line * image_h as usize) / lines).max(start_y + 1); - draw_rect( - buffer, - image_w, - image_h, - image_w - scrollbar_w, - start_y as i32, - scrollbar_w, - end_y as i32 - start_y as i32, - 0x40FFFFFF, + + let rect = Rectangle::new( + [image_w as f32 / scale_factor, start_y as f32 / scale_factor].into(), + Size::new( + scrollbar_w as f32 / scale_factor, + (end_y as f32 - start_y as f32) / scale_factor, + ), ); + state.scrollbar_rect.set(rect); } } @@ -330,16 +349,35 @@ where } 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( renderer, handle, Rectangle::new( - layout.position() + [self.padding.left as f32, self.padding.top as f32].into(), - Size::new(view_w as f32, view_h as f32), + image_position, + Size::new(image_size.width as f32, image_size.height as f32), ), [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(); log::debug!("redraw {}, {}: {:?}", view_w, view_h, duration); } @@ -356,8 +394,10 @@ where _viewport: &Rectangle, ) -> Status { let state = tree.state.downcast_mut::(); - 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 buffer_size = editor.buffer().size(); let mut font_system = FONT_SYSTEM.lock().unwrap(); let mut editor = editor.borrow_with(&mut font_system); @@ -439,27 +479,51 @@ where } Event::Mouse(MouseEvent::ButtonPressed(Button::Left)) => { if let Some(p) = cursor_position.position_in(layout.bounds()) { - editor.action(Action::Click { - x: ((p.x - self.padding.left) * scale_factor) as i32, - y: ((p.y - self.padding.top) * scale_factor) as i32, - }); - state.is_dragging = true; + 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 { + editor.action(Action::Click { + 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; } } Event::Mouse(MouseEvent::ButtonReleased(Button::Left)) => { - state.is_dragging = false; + state.dragging = None; status = Status::Captured; } Event::Mouse(MouseEvent::CursorMoved { .. }) => { - if state.is_dragging { + if let Some(dragging) = &state.dragging { if let Some(p) = cursor_position.position() { - editor.action(Action::Drag { - x: (((p.x - layout.bounds().x) - self.padding.left) * scale_factor) - as i32, - y: (((p.y - layout.bounds().y) - self.padding.top) * scale_factor) - as i32, - }); + let x = ((p.x - layout.bounds().x) - self.padding.left) * scale_factor; + let y = ((p.y - layout.bounds().y) - self.padding.top) * scale_factor; + match dragging { + Dragging::Buffer => { + 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; } @@ -490,10 +554,16 @@ where } } +enum Dragging { + Buffer, + Scrollbar { start_y: f32, start_scroll: i32 }, +} + pub struct State { modifiers: Modifiers, - is_dragging: bool, - scale_factor: Cell, + dragging: Option, + scale_factor: Cell, + scrollbar_rect: Cell>, handle: Mutex, } @@ -502,8 +572,9 @@ impl State { pub fn new() -> State { State { modifiers: Modifiers::empty(), - is_dragging: false, + dragging: None, scale_factor: Cell::new(1.0), + scrollbar_rect: Cell::new(Rectangle::default()), //TODO: make option! handle: Mutex::new(image::Handle::from_pixels(1, 1, vec![0, 0, 0, 0])), }