From f3af86298f01692be53e45f4301f1cad3867f3c3 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Thu, 1 Feb 2024 02:03:13 +0100 Subject: [PATCH] cursors initial version --- .../src/components/rich_text/compositor.rs | 31 +++ .../src/components/rich_text/gfx/device.rs | 223 ---------------- sugarloaf/src/components/rich_text/gfx/gl.rs | 19 -- sugarloaf/src/components/rich_text/gfx/mod.rs | 250 ------------------ sugarloaf/src/components/rich_text/mod.rs | 3 +- sugarloaf/src/components/rich_text/text.rs | 3 + sugarloaf/src/core.rs | 4 +- sugarloaf/src/layout/builder_data.rs | 13 + sugarloaf/src/layout/layout.rs | 17 ++ sugarloaf/src/layout/layout_data.rs | 3 + sugarloaf/src/layout/span_style.rs | 9 + sugarloaf/src/sugarloaf.rs | 43 +-- 12 files changed, 104 insertions(+), 514 deletions(-) delete mode 100644 sugarloaf/src/components/rich_text/gfx/device.rs delete mode 100644 sugarloaf/src/components/rich_text/gfx/gl.rs delete mode 100644 sugarloaf/src/components/rich_text/gfx/mod.rs diff --git a/sugarloaf/src/components/rich_text/compositor.rs b/sugarloaf/src/components/rich_text/compositor.rs index d27e2a99ce..bce3c0b827 100644 --- a/sugarloaf/src/components/rich_text/compositor.rs +++ b/sugarloaf/src/components/rich_text/compositor.rs @@ -4,6 +4,7 @@ pub use crate::components::rich_text::batch::{ pub use crate::components::rich_text::image_cache::{ AddImage, Epoch, ImageData, ImageId, ImageLocation, TextureEvent, TextureId, }; +use crate::core::SugarCursorStyle; use crate::components::rich_text::batch::BatchManager; use crate::components::rich_text::image_cache::{GlyphCache, ImageCache}; @@ -166,6 +167,36 @@ impl Compositor { ); } + if let Some(cursor) = style.cursor { + match cursor.style { + SugarCursorStyle::Block => { + self.batches.add_rect( + &Rect::new( + rect.x, + style.topline, + rect.width, + style.line_height, + ), + depth - 1.0, + &cursor.color, + ); + } + SugarCursorStyle::Caret => { + self.batches.add_rect( + &Rect::new( + rect.x, + style.topline, + 3.0, + style.line_height, + ), + depth - 1.0, + &cursor.color, + ); + } + _ => {} + } + } + if underline { if entry.top - underline_offset < entry.height as i32 { if let Some(mut desc_ink) = entry.desc.range() { diff --git a/sugarloaf/src/components/rich_text/gfx/device.rs b/sugarloaf/src/components/rich_text/gfx/device.rs deleted file mode 100644 index 020a33aaf1..0000000000 --- a/sugarloaf/src/components/rich_text/gfx/device.rs +++ /dev/null @@ -1,223 +0,0 @@ -use super::{gl, Buffer, BufferKind, Texture, Shader}; -use crate::comp::*; -use std::collections::HashMap; - -pub struct Device { - base_shader: BatchShader, - subpx_shader: BatchShader, - vertices: Buffer, - indices: Buffer, - textures: HashMap, -} - -impl Device { - pub fn new() -> Self { - Self { - base_shader: BatchShader::new(shaders::gl::BASE_VS, shaders::gl::BASE_FS), - subpx_shader: BatchShader::new(shaders::gl::BASE_VS, shaders::gl::SUBPIXEL_FS), - vertices: Buffer::new(BufferKind::Array), - indices: Buffer::new(BufferKind::Element), - textures: HashMap::default(), - } - } - - fn handle_texture_event(&mut self, event: &TextureEvent) { - match event { - TextureEvent::CreateTexture { id, format, width, height, data } => { - let tex = Texture::new(*width as u32, *height as u32); - if let Some(data) = data { - tex.update(data); - } - self.textures.insert(*id, tex); - } - TextureEvent::UpdateTexture { id, x, y, width, height, data } => { - if let Some(tex) = self.textures.get(&id) { - tex.update(data); - } - - } - TextureEvent::DestroyTexture(id) => { - self.textures.remove(id); - } - } - } - - pub fn finish_composition(&mut self, compositor: &mut Compositor, display_list: &mut DisplayList) { - compositor.finish(display_list, |e| self.handle_texture_event(&e)); - } - - pub fn render(&mut self, width: u32, height: u32, list: &DisplayList) { - self.vertices.update(list.vertices()); - self.indices.update(list.indices()); - let view_proj = create_view_proj(width, height); - self.vertices.bind(); - self.indices.bind(); - for command in list.commands() { - match command { - Command::BindPipeline(pipeline) => { - match pipeline { - Pipeline::Opaque => { - unsafe { - gl::DepthMask(1); - gl::Disable(gl::BLEND); - gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - gl::BlendEquation(gl::FUNC_ADD); - } - self.base_shader.activate(); - self.base_shader.bind_attribs(); - self.base_shader.set_view_proj(&view_proj); - } - Pipeline::Transparent => { - unsafe { - gl::DepthMask(0); - gl::Enable(gl::BLEND); - gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); - } - self.base_shader.activate(); - self.base_shader.bind_attribs(); - self.base_shader.set_view_proj(&view_proj); - } - Pipeline::Subpixel => { - unsafe { - gl::DepthMask(0); - gl::Enable(gl::BLEND); - gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR); - } - self.subpx_shader.activate(); - self.subpx_shader.bind_attribs(); - self.subpx_shader.set_view_proj(&view_proj); - } - - } - } - Command::BindTexture(unit, id) => { - if let Some(tex) = self.textures.get(&id) { - tex.bind(*unit); - } - } - Command::Draw { start, count } => { - unsafe { - gl::DrawElements( - gl::TRIANGLES, - *count as _, - gl::UNSIGNED_INT, - (*start as usize * 4) as *const _, - ); - } - } - } - } - unsafe { - gl::DepthMask(1); - gl::Disable(gl::BLEND); - } - } -} - -struct BatchShader { - shader: Shader, - v_pos: i32, - v_color: i32, - v_uv: i32, - view_proj: i32, - tex: i32, - mask: i32, -} - -impl BatchShader { - pub fn new(vertex: &str, fragment: &str) -> Self { - let shader = Shader::new(vertex, fragment); - let v_pos = shader.attrib_location("v_pos"); - let v_color = shader.attrib_location("v_color"); - let v_uv = shader.attrib_location("v_uv"); - let view_proj = shader.uniform_location("view_proj"); - let tex = shader.uniform_location("tex"); - let mask = shader.uniform_location("mask"); - shader.activate(); - unsafe { - gl::Uniform1i(tex, 1); - gl::Uniform1i(mask, 0); - } - Self { - shader, - v_pos, - v_color, - v_uv, - view_proj, - tex, - mask, - } - } - - pub fn activate(&self) { - self.shader.activate(); - } - - pub fn set_view_proj(&self, view_proj: &[f32]) { - unsafe { - gl::UniformMatrix4fv(self.view_proj as _, 1, 0, view_proj.as_ptr()); - } - } - - pub fn bind_attribs(&self) { - unsafe { - gl::VertexAttribPointer( - self.v_pos as _, - 4, - gl::FLOAT, - 0, - 28, - core::ptr::null(), - ); - gl::VertexAttribPointer( - self.v_color as _, - 4, - gl::UNSIGNED_BYTE, - 1, - 28, - 16usize as *const _, - ); - gl::VertexAttribPointer( - self.v_uv as _, - 2, - gl::FLOAT, - 0, - 28, - 20usize as *const _, - ); - gl::EnableVertexAttribArray(self.v_pos as _); - gl::EnableVertexAttribArray(self.v_color as _); - gl::EnableVertexAttribArray(self.v_uv as _); - } - } -} - -fn create_view_proj(width: u32, height: u32) -> [f32; 16] { - let r = width as f32; - let l = 0.; - let t = 0.; - let b = height as f32; - let n = 0.1; - let f = 1024.; - [ - 2. / (r - l), - 0., - 0., - (l + r) / (l - r), - // - 0., - 2. / (t - b), - 0., - (t + b) / (b - t), - // - 0., - 0., - 2. / (f - n), - -(f + n) / (f - n), - // - 0., - 0., - 0., - 1., - ] -} diff --git a/sugarloaf/src/components/rich_text/gfx/gl.rs b/sugarloaf/src/components/rich_text/gfx/gl.rs deleted file mode 100644 index 1a435a1104..0000000000 --- a/sugarloaf/src/components/rich_text/gfx/gl.rs +++ /dev/null @@ -1,19 +0,0 @@ -use glutin::{self, PossiblyCurrent}; -use std::ffi::CStr; - -include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); - -pub fn load(gl_context: &glutin::Context) { - load_with(|ptr| gl_context.get_proc_address(ptr) as *const _); - let version = unsafe { - let data = CStr::from_ptr(GetString(VERSION) as *const _).to_bytes().to_vec(); - String::from_utf8(data).unwrap() - }; - let mut vao = 0; - unsafe { - GenVertexArrays(1, &mut vao); - BindVertexArray(vao); - } - println!("OpenGL version {}", version); -} - diff --git a/sugarloaf/src/components/rich_text/gfx/mod.rs b/sugarloaf/src/components/rich_text/gfx/mod.rs deleted file mode 100644 index f5737b5acf..0000000000 --- a/sugarloaf/src/components/rich_text/gfx/mod.rs +++ /dev/null @@ -1,250 +0,0 @@ -use std::ffi::CString; - -pub mod gl; - -mod device; - -pub use device::Device; - -use gl::types::*; - -#[derive(Copy, Clone)] -pub enum BufferKind { - Array, - Element, - Uniform, -} - -impl BufferKind { - fn to_gl(self) -> GLenum { - match self { - Self::Array => gl::ARRAY_BUFFER, - Self::Element => gl::ELEMENT_ARRAY_BUFFER, - Self::Uniform => gl::UNIFORM_BUFFER, - } - } -} - -pub struct Buffer { - id: GLuint, - kind: BufferKind, - capacity: usize, -} - -impl Buffer { - pub fn new(kind: BufferKind) -> Self { - unsafe { - let mut id = 0; - gl::GenBuffers(1, &mut id); - Self { - id, - kind, - capacity: 0, - } - } - } - - pub fn update(&mut self, data: &[T]) { - let size = data.len() * core::mem::size_of::(); - if size > isize::MAX as usize { - return; - } - let ty = self.kind.to_gl(); - unsafe { - gl::BindBuffer(ty, self.id); - if size > self.capacity { - gl::BufferData(ty, size as isize, data.as_ptr() as *const _, gl::DYNAMIC_DRAW); - self.capacity = size; - } else { - gl::BufferSubData(ty, 0, size as isize, data.as_ptr() as *const _); - } - } - } - - pub fn update_slice(&mut self, offset: usize, data: &[T]) { - let size = data.len() * core::mem::size_of::(); - let ty = self.kind.to_gl(); - if let Some(end) = offset.checked_add(size) { - if end <= self.capacity && end < isize::MAX as usize { - unsafe { - gl::BindBuffer(ty, self.id); - gl::BufferSubData(ty, offset as _, size as isize, data.as_ptr() as *const _); - } - } - } - } - - pub fn bind(&self) { - unsafe { - gl::BindBuffer(self.kind.to_gl(), self.id); - } - } -} - -impl Drop for Buffer { - fn drop(&mut self) { - unsafe { - gl::DeleteBuffers(1, &self.id); - } - } -} - -pub struct Texture { - pub id: GLuint, - pub width: u32, - pub height: u32, -} - -impl Texture { - pub fn new(width: u32, height: u32) -> Self { - let mut id = 0; - unsafe { - gl::GenTextures(1, &mut id); - gl::BindTexture(gl::TEXTURE_2D, id); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as _); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as _); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as _); - gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as _); - gl::TexImage2D( - gl::TEXTURE_2D, - 0, - gl::RGBA as _, - width as i32, - height as i32, - 0, - gl::RGBA, - gl::UNSIGNED_BYTE, - core::ptr::null(), - ); - } - Self { id, width, height } - } - - pub fn width(&self) -> u32 { - self.width - } - - pub fn height(&self) -> u32 { - self.height - } - - pub fn update(&self, data: &[T]) { - unsafe { - gl::BindTexture(gl::TEXTURE_2D, self.id); - gl::TexSubImage2D(gl::TEXTURE_2D, 0, 0, 0, self.width as i32, self.height as i32, gl::RGBA, gl::UNSIGNED_BYTE, data.as_ptr() as *const _); - // gl::TexImage2D( - // gl::TEXTURE_2D, - // 0, - // gl::RGBA as _, - // self.width as i32, - // self.height as i32, - // 0, - // gl::RGBA, - // gl::UNSIGNED_BYTE, - // data.as_ptr() as *const _, - // ); - } - } - - pub fn bind(&self, unit: u32) { - let val = gl::TEXTURE0 + unit; - unsafe { - gl::ActiveTexture(val); - gl::BindTexture(gl::TEXTURE_2D, self.id); - gl::ActiveTexture(gl::TEXTURE0); - } - } -} - -impl Drop for Texture { - fn drop(&mut self) { - unsafe { - gl::DeleteTextures(1, &self.id); - } - } -} - -pub struct Shader { - pub id: GLuint, -} - -unsafe fn compile_shader(kind: GLenum, source: &str) -> GLuint { - let src = CString::new(source).unwrap(); - let s = gl::CreateShader(kind); - gl::ShaderSource( - s, - 1, - [src.as_ptr() as *const _].as_ptr(), - std::ptr::null(), - ); - gl::CompileShader(s); - let mut status = 0; - gl::GetShaderiv(s, gl::COMPILE_STATUS, &mut status); - if status == 0 { - let mut info: Vec = Vec::new(); - let mut size = 0; - gl::GetShaderiv(s, gl::INFO_LOG_LENGTH, &mut size); - info.resize(size as usize, 0); - let mut ignored = 0; - gl::GetShaderInfoLog(s, size as _, &mut ignored, info.as_mut_ptr() as *mut _); - if let Ok(s) = core::str::from_utf8(&info) { - println!("{}", s); - } - } - s -} - -impl Shader { - pub fn new(vertex: &str, fragment: &str) -> Self { - let x = gl::ONE_MINUS_SRC1_COLOR; - unsafe { - let id = gl::CreateProgram(); - let vs = compile_shader(gl::VERTEX_SHADER, vertex); - let fs = compile_shader(gl::FRAGMENT_SHADER, fragment); - gl::AttachShader(id, vs); - gl::AttachShader(id, fs); - gl::LinkProgram(id); - let mut status = 0; - gl::GetProgramiv(id, gl::LINK_STATUS, &mut status); - if status == 0 { - let mut info: Vec = Vec::new(); - let mut size = 0; - gl::GetProgramiv(id, gl::INFO_LOG_LENGTH, &mut size); - info.resize(size as usize, 0); - let mut ignored = 0; - gl::GetProgramInfoLog(id, size as _, &mut ignored, info.as_mut_ptr() as *mut _); - if let Ok(s) = core::str::from_utf8(&info) { - println!("{}", s); - } - } - gl::DeleteShader(vs); - gl::DeleteShader(fs); - Self { id } - } - } - - pub fn attrib_location(&self, name: &str) -> i32 { - let name = CString::new(name).unwrap(); - unsafe { gl::GetAttribLocation(self.id, name.as_ptr() as *const _) } - } - - pub fn uniform_location(&self, name: &str) -> i32 { - let name = CString::new(name).unwrap(); - unsafe { gl::GetUniformLocation(self.id, name.as_ptr() as *const _) } - } - - pub fn activate(&self) { - unsafe { - gl::UseProgram(self.id); - } - } -} - -impl Drop for Shader { - fn drop(&mut self) { - unsafe { - gl::DeleteProgram(self.id); - } - } -} - diff --git a/sugarloaf/src/components/rich_text/mod.rs b/sugarloaf/src/components/rich_text/mod.rs index bdc571099e..6e8a3e068c 100644 --- a/sugarloaf/src/components/rich_text/mod.rs +++ b/sugarloaf/src/components/rich_text/mod.rs @@ -777,6 +777,7 @@ fn draw_layout(comp: &mut compositor::Compositor, layout: &Paragraph, x: f32, y: font_coords: run.normalized_coords(), font_size: run.font_size(), color, + cursor: run.cursor(), background_color: run.background_color(), baseline: py, topline: py - line.ascent(), @@ -786,7 +787,7 @@ fn draw_layout(comp: &mut compositor::Compositor, layout: &Paragraph, x: f32, y: Some(UnderlineStyle { offset: run.underline_offset(), size: run.underline_size(), - color, + color: run.underline_color(), }) } else { None diff --git a/sugarloaf/src/components/rich_text/text.rs b/sugarloaf/src/components/rich_text/text.rs index 3e568ab191..4d19a4a977 100644 --- a/sugarloaf/src/components/rich_text/text.rs +++ b/sugarloaf/src/components/rich_text/text.rs @@ -1,3 +1,4 @@ +use crate::core::SugarCursor; use swash::{FontRef, GlyphId, NormalizedCoord}; /// Properties for a text run. @@ -23,6 +24,8 @@ pub struct TextRunStyle<'a> { pub advance: f32, /// Underline style. pub underline: Option, + /// Cursor style. + pub cursor: Option, } /// Underline decoration style. diff --git a/sugarloaf/src/core.rs b/sugarloaf/src/core.rs index 02e7b24e40..61aa8cb130 100644 --- a/sugarloaf/src/core.rs +++ b/sugarloaf/src/core.rs @@ -16,14 +16,14 @@ pub struct Sugar { pub media: Option, } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum SugarCursorStyle { Block, Caret, Underline, } -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub struct SugarCursor { pub color: [f32; 4], pub style: SugarCursorStyle, diff --git a/sugarloaf/src/layout/builder_data.rs b/sugarloaf/src/layout/builder_data.rs index 07994f1546..5f0e1d9ff8 100644 --- a/sugarloaf/src/layout/builder_data.rs +++ b/sugarloaf/src/layout/builder_data.rs @@ -3,6 +3,7 @@ use super::{ font::internal::{FontContext, FontGroupId}, SpanId, MAX_ID, }; +use crate::core::SugarCursor; use core::borrow::Borrow; use swash::text::{cluster::CharInfo, Script}; use swash::{Setting, Stretch, Style, Weight}; @@ -90,10 +91,14 @@ pub struct SpanData { pub underline: bool, /// Offset of an underline. pub underline_offset: Option, + /// Color of an underline. + pub underline_color: Option<[f32; 4]>, /// Thickness of an underline. pub underline_size: Option, /// Text case transformation. pub text_transform: TextTransform, + /// Cursor + pub cursor: Option, } /// Builder state. @@ -165,8 +170,10 @@ impl BuilderState { line_spacing: 1., color: [1.0, 1.0, 1.0, 1.0], background_color: None, + cursor: None, underline: false, underline_offset: None, + underline_color: None, underline_size: None, text_transform: TextTransform::None, }); @@ -251,6 +258,9 @@ impl BuilderState { S::Size(size) => { span.font_size = *size * scale; } + S::Cursor(cursor) => { + span.cursor = Some(*cursor); + } S::Color(color) => { span.color = *color; } @@ -278,6 +288,9 @@ impl BuilderState { S::UnderlineOffset(offset) => { span.underline_offset = (*offset).map(|o| o * scale); } + S::UnderlineColor(color) => { + span.underline_color = Some(*color); + } S::UnderlineSize(size) => { span.underline_size = (*size).map(|s| s * scale) } diff --git a/sugarloaf/src/layout/layout.rs b/sugarloaf/src/layout/layout.rs index c2cad5d045..70e99f1d6f 100644 --- a/sugarloaf/src/layout/layout.rs +++ b/sugarloaf/src/layout/layout.rs @@ -4,6 +4,7 @@ use super::layout_data::*; use super::line_breaker::BreakLines; use super::Direction; use super::{builder_data::SpanData, font::Font, Paragraph, SpanId}; +use crate::core::SugarCursor; use core::iter::DoubleEndedIterator; use core::ops::Range; use swash::shape::{cluster::Glyph as ShapedGlyph, Shaper}; @@ -91,7 +92,11 @@ impl Paragraph { ascent: metrics.ascent * span_data.line_spacing, descent: metrics.descent * span_data.line_spacing, leading: metrics.leading * span_data.line_spacing, + cursor: span_data.cursor, underline: span_data.underline, + underline_color: span_data + .underline_color + .unwrap_or(span_data.color), underline_offset: span_data .underline_offset .unwrap_or(metrics.underline_offset), @@ -188,7 +193,9 @@ impl Paragraph { leading: metrics.leading * span_data.line_spacing, color: span_data.color, background_color: span_data.background_color, + cursor: span_data.cursor, underline: span_data.underline, + underline_color: span_data.underline_color.unwrap_or(span_data.color), underline_offset: span_data .underline_offset .unwrap_or(metrics.underline_offset), @@ -312,6 +319,11 @@ impl<'a> Run<'a> { self.run.level } + /// Returns the cursor + pub fn cursor(&self) -> Option { + self.run.cursor + } + /// Returns the direction of the run. pub fn direction(&self) -> Direction { if self.run.level & 1 != 0 { @@ -349,6 +361,11 @@ impl<'a> Run<'a> { self.run.underline_offset } + /// Returns the underline color for the run. + pub fn underline_color(&self) -> [f32; 4] { + self.run.underline_color + } + /// Returns the underline size for the run. pub fn underline_size(&self) -> f32 { self.run.underline_size diff --git a/sugarloaf/src/layout/layout_data.rs b/sugarloaf/src/layout/layout_data.rs index 37fa6526a1..16e7e08e76 100644 --- a/sugarloaf/src/layout/layout_data.rs +++ b/sugarloaf/src/layout/layout_data.rs @@ -1,6 +1,7 @@ use super::Alignment; use super::Glyph; use super::{font::Font, SpanId}; +use crate::core::SugarCursor; use swash::text::cluster::ClusterInfo; /// Cluster represents multiple glyphs. @@ -183,9 +184,11 @@ pub struct RunData { pub underline: bool, pub underline_offset: f32, pub underline_size: f32, + pub underline_color: [f32; 4], pub strikeout_offset: f32, pub strikeout_size: f32, pub advance: f32, + pub cursor: Option, } #[derive(Clone, Default)] diff --git a/sugarloaf/src/layout/span_style.rs b/sugarloaf/src/layout/span_style.rs index 4bfbd10593..5227db1afb 100644 --- a/sugarloaf/src/layout/span_style.rs +++ b/sugarloaf/src/layout/span_style.rs @@ -1,4 +1,5 @@ pub use super::font::FamilyList; +use crate::core::SugarCursor; pub use swash::text::Language; use swash::{Setting, Stretch, Style, Weight}; @@ -21,6 +22,8 @@ pub enum SpanStyle<'a> { BackgroundColor([f32; 4]), /// Font style. Style(Style), + /// Cursor. + Cursor(SugarCursor), /// Font feature settings. Features(Cow<'a, [Setting]>), /// Font variation settings. @@ -37,6 +40,8 @@ pub enum SpanStyle<'a> { LineSpacing(f32), /// Underline decoration. Underline(bool), + /// Underline color. + UnderlineColor([f32; 4]), /// Offset of an underline. Set to `None` to use the font value. UnderlineOffset(Option), /// Thickness of an underline. Set to `None` to use the font value. @@ -77,8 +82,10 @@ impl<'a> SpanStyle<'a> { Self::LineSpacing(v) => S::LineSpacing(*v), Self::Underline(v) => S::Underline(*v), Self::UnderlineOffset(v) => S::UnderlineOffset(*v), + Self::UnderlineColor(v) => S::UnderlineColor(*v), Self::UnderlineSize(v) => S::UnderlineSize(*v), Self::TextTransform(v) => S::TextTransform(*v), + Self::Cursor(v) => S::Cursor(*v), } } @@ -101,8 +108,10 @@ impl<'a> SpanStyle<'a> { Self::LineSpacing(v) => S::LineSpacing(v), Self::Underline(v) => S::Underline(v), Self::UnderlineOffset(v) => S::UnderlineOffset(v), + Self::UnderlineColor(v) => S::UnderlineColor(v), Self::UnderlineSize(v) => S::UnderlineSize(v), Self::TextTransform(v) => S::TextTransform(v), + Self::Cursor(v) => S::Cursor(v), } } diff --git a/sugarloaf/src/sugarloaf.rs b/sugarloaf/src/sugarloaf.rs index 0ccd4921c1..f475527452 100644 --- a/sugarloaf/src/sugarloaf.rs +++ b/sugarloaf/src/sugarloaf.rs @@ -421,29 +421,34 @@ impl Sugarloaf { } } - // let mut has_underline_cursor = false; - // if let Some(cursor) = &stack[i].cursor { - // match cursor.style { - // SugarCursorStyle::Underline => { - // let underline_cursor = &[ - // SpanStyle::UnderlineColor(cursor.color), - // SpanStyle::Underline(true), - // SpanStyle::UnderlineOffset(Some(-1.)), - // SpanStyle::UnderlineSize(Some(1.)), - // ]; - // self.content.enter_span(underline_cursor); - // span_counter += 1; - // has_underline_cursor = true; - // }, - // _ => {} - // } - // } + let mut has_underline_cursor = false; + if let Some(cursor) = &stack[i].cursor { + match cursor.style { + SugarCursorStyle::Underline => { + let underline_cursor = &[ + SpanStyle::UnderlineColor(cursor.color), + SpanStyle::Underline(true), + SpanStyle::UnderlineOffset(Some(-1.)), + SpanStyle::UnderlineSize(Some(2.)), + ]; + self.content.enter_span(underline_cursor); + span_counter += 1; + has_underline_cursor = true; + } + SugarCursorStyle::Block | SugarCursorStyle::Caret => { + self.content.enter_span(&[SpanStyle::Cursor(*cursor)]); + span_counter += 1; + } + } + } if let Some(decoration) = &stack[i].decoration { match decoration { SugarDecoration::Underline => { - self.content.enter_span(underline); - span_counter += 1; + if !has_underline_cursor { + self.content.enter_span(underline); + span_counter += 1; + } } SugarDecoration::Strikethrough => { self.content.enter_span(strikethrough);