diff --git a/Cargo.lock b/Cargo.lock index 3e73707..2383f5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1540,8 +1540,8 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.14.2" -source = "git+https://github.com/pop-os/cosmic-text.git#cffdea2b334e7830a5fd6f95bf5e1784014442a8" +version = "0.15.0" +source = "git+https://github.com/pop-os/cosmic-text.git#9339446cfa9b7f0110094a97764dccc09cfa98a2" dependencies = [ "bitflags 2.10.0", "cosmic_undo_2", @@ -1883,7 +1883,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -2113,7 +2113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2679,7 +2679,7 @@ dependencies = [ "gobject-sys", "libc", "system-deps 7.0.6", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4314,7 +4314,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d463f34ca3c400fde3a054da0e0b8c6ffa21e4590922f3e18281bb5eeef4cbdc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5383,7 +5383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.45.0", ] [[package]] @@ -6391,7 +6391,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6976,7 +6976,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8021,7 +8021,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/src/text_box.rs b/src/text_box.rs index c0cd7ff..6526ed5 100644 --- a/src/text_box.rs +++ b/src/text_box.rs @@ -5,18 +5,19 @@ use cosmic::{ cosmic_theme::palette::{WithAlpha, blend::Compose}, iced::{ Color, Element, Length, Padding, Point, Rectangle, Size, Vector, - advanced::graphics::text::font_system, + advanced::graphics::text::{Raw, font_system}, event::{Event, Status}, keyboard::{Event as KeyEvent, Modifiers}, mouse::{self, Button, Event as MouseEvent, ScrollDelta}, }, iced_core::{ - Border, Radians, Shell, + Border, Radians, Shell, Transformation, clipboard::Clipboard, image, keyboard::{Key, key::Named}, layout::{self, Layout}, renderer::{self, Quad, Renderer as _}, + text::Renderer as _, widget::{ self, Id, Widget, operation::{self, Operation}, @@ -26,12 +27,13 @@ use cosmic::{ theme::Theme, }; use cosmic_text::{ - Action, BorrowedWithFontSystem, Edit, Metrics, Motion, Scroll, Selection, ViEditor, + Action, BorrowedWithFontSystem, Edit, Metrics, Motion, Renderer as _, Scroll, Selection, + ViEditor, }; use std::{ cell::Cell, cmp, - sync::Mutex, + sync::{Arc, Mutex}, time::{Duration, Instant}, }; @@ -137,11 +139,13 @@ where TextBox::new(editor, metrics) } +#[derive(Clone, Copy)] struct Canvas { w: i32, h: i32, } +#[derive(Clone, Copy)] struct Offset { x: i32, y: i32, @@ -228,6 +232,30 @@ fn draw_rect( } } +struct CustomRenderer<'a> { + renderer: &'a mut Renderer, + pos: Point, +} + +impl<'a> cosmic_text::Renderer for CustomRenderer<'a> { + fn rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, color: cosmic_text::Color) { + self.renderer.fill_quad( + Quad { + bounds: Rectangle::new( + self.pos + Vector::new(x as f32, y as f32), + Size::new(w as f32, h as f32), + ), + ..Default::default() + }, + Color::from_rgba8(color.r(), color.g(), color.b(), (color.a() as f32) / 255.0), + ); + } + + fn glyph(&mut self, _physical_glyph: cosmic_text::PhysicalGlyph, _color: cosmic_text::Color) { + // Glyphs will be drawn by iced fill_raw for performance + } +} + impl<'a, Message> Widget for TextBox<'a, Message> where Message: Clone, @@ -341,6 +369,7 @@ where let cosmic_theme = theme.cosmic(); let scrollbar_w = cosmic_theme.spacing.space_xxs as i32; + 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) - self.padding.horizontal() as i32 - scrollbar_w; @@ -370,8 +399,8 @@ where (image, scaled) }; - let (image_w, scaled_w) = calculate_ideal(view_w); - let (image_h, scaled_h) = calculate_ideal(view_h); + let (image_w, _scaled_w) = calculate_ideal(view_w); + let (image_h, _scaled_h) = calculate_ideal(view_h); if image_w <= 0 || image_h <= 0 { // Zero sized image @@ -437,9 +466,13 @@ where editor.shape_as_needed(font_system.raw(), true); let mut handle_opt = state.handle_opt.lock().unwrap(); + let image_canvas = Canvas { + w: editor_offset_x, + h: image_h, + }; if editor.redraw() || handle_opt.is_none() { // Draw to pixel buffer - let mut pixels_u8 = vec![0; image_w as usize * image_h as usize * 4]; + let mut pixels_u8 = vec![0; image_canvas.w as usize * image_canvas.h as usize * 4]; { let mut swash_cache = SWASH_CACHE.get().unwrap().lock().unwrap(); @@ -450,6 +483,7 @@ where ) }; + //TODO: draw line numbers using iced functions for performance if self.line_numbers { let (gutter, gutter_foreground) = { let convert_color = |color: syntect::highlighting::Color| { @@ -470,10 +504,7 @@ where // Ensure fill with gutter color draw_rect( pixels, - Canvas { - w: image_w, - h: image_h, - }, + image_canvas, Canvas { w: editor_offset_x, h: image_h, @@ -527,10 +558,7 @@ where |x, y, color| { draw_rect( pixels, - Canvas { - w: image_w, - h: image_h, - }, + image_canvas, Canvas { w: 1, h: 1 }, Offset { x: physical_glyph.x + x, @@ -546,71 +574,6 @@ where }); } - if self.highlight_current_line { - 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 cursor = editor.cursor(); - editor.with_buffer(|buffer| { - for run in buffer.layout_runs() { - if run.line_i != cursor.line { - continue; - } - - draw_rect( - pixels, - Canvas { - w: image_w, - h: image_h, - }, - Canvas { - w: image_w - editor_offset_x, - h: metrics.line_height as i32, - }, - Offset { - x: editor_offset_x, - y: run.line_top as i32, - }, - line_highlight, - ); - } - }); - } - - // Draw editor - let scroll_x = editor.with_buffer(|buffer| buffer.scroll().horizontal as i32); - editor.draw(font_system.raw(), &mut swash_cache, |x, y, w, h, color| { - if x < scroll_x { - //TODO: modify width? - return; - } - draw_rect( - pixels, - Canvas { - w: image_w, - h: image_h, - }, - Canvas { - w: w as i32, - h: h as i32, - }, - Offset { - x: editor_offset_x + x - scroll_x, - y, - }, - color, - ); - }); - // Calculate scrollbar editor.with_buffer(|buffer| { let mut start_line_opt = None; @@ -668,32 +631,102 @@ where state.scale_factor.set(scale_factor); *handle_opt = Some(image::Handle::from_rgba( - image_w as u32, - image_h as u32, + image_canvas.w as u32, + image_canvas.h as u32, pixels_u8, )); } + // Draw cached image let image_position = layout.position() + [self.padding.left, self.padding.top].into(); - if let Some(ref handle) = *handle_opt { - let image_size = image::Renderer::measure_image(renderer, handle); - let scaled_size = Size::new(scaled_w as f32, scaled_h as f32); - log::debug!( - "text_box image {:?} scaled {:?} position {:?}", - image_size, - scaled_size, - image_position - ); - image::Renderer::draw_image( - renderer, - handle.clone(), - image::FilterMethod::Nearest, - Rectangle::new(image_position, scaled_size), - Radians(0.0), - 1.0, - [0.0; 4], - ); - } + + // Draw editor UI + renderer.with_translation(Vector::new(view_position.x, view_position.y), |renderer| { + renderer.with_transformation(Transformation::scale(1.0 / scale_factor), |renderer| { + renderer.with_layer( + Rectangle::new( + Point::new(0.0, 0.0), + Size::new(image_w as f32, image_h as f32), + ), + |renderer| { + // Draw cached image (only has line numbers) + if let Some(ref handle) = *handle_opt { + let image_size = image::Renderer::measure_image(renderer, handle); + image::Renderer::draw_image( + renderer, + handle.clone(), + image::FilterMethod::Nearest, + Rectangle::new( + 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 + let scroll_x = editor.with_buffer(|buffer| buffer.scroll().horizontal); + let pos = Point::new(editor_offset_x as f32, 0.0); + let size = Size::new((image_w - editor_offset_x) as f32, image_h as f32); + + // Create custom renderer for rectangles + let mut custom_renderer = CustomRenderer { renderer, pos }; + + // Draw line highlight + if self.highlight_current_line { + 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 cursor = editor.cursor(); + editor.with_buffer(|buffer| { + for run in buffer.layout_runs() { + if run.line_i != cursor.line { + continue; + } + + 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 - 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"); + } + } + }, + ) + }) + }); // Draw vertical scrollbar {