Move color and x_opt out of Cursor
This commit is contained in:
parent
04d53ac5f4
commit
ae030e9885
12 changed files with 645 additions and 546 deletions
|
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
cosmic-text = { path = "../../", features = ["syntect"] }
|
||||
cosmic-text = { path = "../../", features = ["syntect", "vi"] }
|
||||
env_logger = "0.10"
|
||||
fontdb = "0.13"
|
||||
lazy_static = "1.4"
|
||||
|
|
@ -26,4 +26,4 @@ version = "0.11"
|
|||
|
||||
[features]
|
||||
default = []
|
||||
vi = ["cosmic-text/vi"]
|
||||
vi = []
|
||||
|
|
|
|||
|
|
@ -86,9 +86,6 @@ pub struct Window {
|
|||
path_opt: Option<PathBuf>,
|
||||
attrs: Attrs<'static>,
|
||||
font_size: FontSize,
|
||||
#[cfg(not(feature = "vi"))]
|
||||
editor: Mutex<SyntaxEditor<'static>>,
|
||||
#[cfg(feature = "vi")]
|
||||
editor: Mutex<cosmic_text::ViEditor<'static>>,
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +130,7 @@ impl Application for Window {
|
|||
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
|
||||
let attrs = cosmic_text::Attrs::new().family(cosmic_text::Family::Monospace);
|
||||
|
||||
let mut editor = SyntaxEditor::new(
|
||||
let editor = SyntaxEditor::new(
|
||||
Buffer::new(
|
||||
&mut FONT_SYSTEM.lock().unwrap(),
|
||||
FontSize::Body.to_metrics(),
|
||||
|
|
@ -143,8 +140,8 @@ impl Application for Window {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(feature = "vi")]
|
||||
let mut editor = cosmic_text::ViEditor::new(editor);
|
||||
editor.set_passthrough(cfg!(feature = "vi"));
|
||||
|
||||
update_attrs(&mut editor, attrs);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,14 +5,13 @@ use cosmic::{
|
|||
iced_runtime::keyboard::KeyCode,
|
||||
theme::{Theme, ThemeType},
|
||||
};
|
||||
use cosmic_text::{Action, Edit, Motion, SwashCache};
|
||||
use cosmic_text::{Action, Edit, Motion, SwashCache, ViEditor};
|
||||
use std::{cmp, sync::Mutex, time::Instant};
|
||||
|
||||
use crate::FONT_SYSTEM;
|
||||
|
||||
pub struct Appearance {
|
||||
background_color: Option<Color>,
|
||||
text_color: Color,
|
||||
}
|
||||
|
||||
pub trait StyleSheet {
|
||||
|
|
@ -24,23 +23,21 @@ impl StyleSheet for Theme {
|
|||
match self.theme_type {
|
||||
ThemeType::Dark | ThemeType::HighContrastDark | ThemeType::Custom(_) => Appearance {
|
||||
background_color: Some(Color::from_rgb8(0x34, 0x34, 0x34)),
|
||||
text_color: Color::from_rgb8(0xFF, 0xFF, 0xFF),
|
||||
},
|
||||
ThemeType::Light | ThemeType::HighContrastLight => Appearance {
|
||||
background_color: Some(Color::from_rgb8(0xFC, 0xFC, 0xFC)),
|
||||
text_color: Color::from_rgb8(0x00, 0x00, 0x00),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextBox<'a, Editor> {
|
||||
editor: &'a Mutex<Editor>,
|
||||
pub struct TextBox<'a, 'editor> {
|
||||
editor: &'a Mutex<ViEditor<'editor>>,
|
||||
padding: Padding,
|
||||
}
|
||||
|
||||
impl<'a, Editor> TextBox<'a, Editor> {
|
||||
pub fn new(editor: &'a Mutex<Editor>) -> Self {
|
||||
impl<'a, 'editor> TextBox<'a, 'editor> {
|
||||
pub fn new(editor: &'a Mutex<ViEditor<'editor>>) -> Self {
|
||||
Self {
|
||||
editor,
|
||||
padding: Padding::new(0.),
|
||||
|
|
@ -53,7 +50,7 @@ impl<'a, Editor> TextBox<'a, Editor> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn text_box<'a, Editor>(editor: &'a Mutex<Editor>) -> TextBox<'a, Editor> {
|
||||
pub fn text_box<'a, 'editor>(editor: &'a Mutex<ViEditor<'editor>>) -> TextBox<'a, 'editor> {
|
||||
TextBox::new(editor)
|
||||
}
|
||||
|
||||
|
|
@ -106,11 +103,10 @@ fn draw_pixel(
|
|||
buffer[offset + 3] = (current >> 24) as u8;
|
||||
}
|
||||
|
||||
impl<'a, 'editor, Editor, Message, Renderer> Widget<Message, Renderer> for TextBox<'a, Editor>
|
||||
impl<'a, 'editor, Message, Renderer> Widget<Message, Renderer> for TextBox<'a, 'editor>
|
||||
where
|
||||
Renderer: cosmic::iced_core::Renderer + image::Renderer<Handle = image::Handle>,
|
||||
Renderer::Theme: StyleSheet,
|
||||
Editor: Edit,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
|
|
@ -193,13 +189,6 @@ where
|
|||
);
|
||||
}
|
||||
|
||||
let text_color = cosmic_text::Color::rgba(
|
||||
cmp::max(0, cmp::min(255, (appearance.text_color.r * 255.0) as i32)) as u8,
|
||||
cmp::max(0, cmp::min(255, (appearance.text_color.g * 255.0) as i32)) as u8,
|
||||
cmp::max(0, cmp::min(255, (appearance.text_color.b * 255.0) as i32)) as u8,
|
||||
cmp::max(0, cmp::min(255, (appearance.text_color.a * 255.0) as i32)) as u8,
|
||||
);
|
||||
|
||||
let mut editor = self.editor.lock().unwrap();
|
||||
|
||||
let view_w = cmp::min(viewport.width as i32, layout.bounds().width as i32)
|
||||
|
|
@ -229,18 +218,14 @@ where
|
|||
|
||||
// Draw to pixel buffer
|
||||
let mut pixels = vec![0; image_w as usize * image_h as usize * 4];
|
||||
editor.draw(
|
||||
&mut state.cache.lock().unwrap(),
|
||||
text_color,
|
||||
|x, y, w, h, color| {
|
||||
//TODO: improve performance
|
||||
for row in 0..h as i32 {
|
||||
for col in 0..w as i32 {
|
||||
draw_pixel(&mut pixels, image_w, image_h, x + col, y + row, color);
|
||||
}
|
||||
editor.draw(&mut state.cache.lock().unwrap(), |x, y, w, h, color| {
|
||||
//TODO: improve performance
|
||||
for row in 0..h as i32 {
|
||||
for col in 0..w as i32 {
|
||||
draw_pixel(&mut pixels, image_w, image_h, x + col, y + row, color);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Restore original metrics
|
||||
editor.buffer_mut().set_metrics(metrics);
|
||||
|
|
@ -370,14 +355,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'editor, Editor, Message, Renderer> From<TextBox<'a, Editor>>
|
||||
for Element<'a, Message, Renderer>
|
||||
impl<'a, 'editor, Message, Renderer> From<TextBox<'a, 'editor>> for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: renderer::Renderer + image::Renderer<Handle = image::Handle>,
|
||||
Renderer::Theme: StyleSheet,
|
||||
Editor: Edit,
|
||||
{
|
||||
fn from(text_box: TextBox<'a, Editor>) -> Self {
|
||||
fn from(text_box: TextBox<'a, 'editor>) -> Self {
|
||||
Self::new(text_box)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,8 +95,7 @@ fn main() {
|
|||
let bg = editor.background_color();
|
||||
window.set(orbclient::Color::rgb(bg.r(), bg.g(), bg.b()));
|
||||
|
||||
let fg = editor.foreground_color();
|
||||
editor.draw(&mut swash_cache, fg, |x, y, w, h, color| {
|
||||
editor.draw(&mut swash_cache, |x, y, w, h, color| {
|
||||
window.rect(
|
||||
line_x as i32 + x,
|
||||
y,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ fn redraw(
|
|||
) {
|
||||
let bg_color = orbclient::Color::rgb(0x34, 0x34, 0x34);
|
||||
let font_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
||||
let cursor_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
||||
let selection_color = Color::rgba(0xFF, 0xFF, 0xFF, 0x33);
|
||||
|
||||
editor.shape_as_needed(true);
|
||||
if editor.buffer().redraw() {
|
||||
|
|
@ -22,9 +24,15 @@ fn redraw(
|
|||
|
||||
window.set(bg_color);
|
||||
|
||||
editor.draw(swash_cache, font_color, |x, y, w, h, color| {
|
||||
window.rect(x, y, w, h, orbclient::Color { data: color.0 });
|
||||
});
|
||||
editor.draw(
|
||||
swash_cache,
|
||||
font_color,
|
||||
cursor_color,
|
||||
selection_color,
|
||||
|x, y, w, h, color| {
|
||||
window.rect(x, y, w, h, orbclient::Color { data: color.0 });
|
||||
},
|
||||
);
|
||||
|
||||
window.sync();
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ fn main() {
|
|||
loop {
|
||||
let bg_color = orbclient::Color::rgb(0x34, 0x34, 0x34);
|
||||
let font_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
||||
let cursor_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
||||
let selection_color = Color::rgba(0xFF, 0xFF, 0xFF, 0x33);
|
||||
|
||||
editor.shape_as_needed(true);
|
||||
if editor.buffer().redraw() {
|
||||
|
|
@ -137,9 +139,15 @@ fn main() {
|
|||
|
||||
window.set(bg_color);
|
||||
|
||||
editor.draw(&mut swash_cache, font_color, |x, y, w, h, color| {
|
||||
window.rect(x, y, w, h, orbclient::Color { data: color.0 });
|
||||
});
|
||||
editor.draw(
|
||||
&mut swash_cache,
|
||||
font_color,
|
||||
cursor_color,
|
||||
selection_color,
|
||||
|x, y, w, h, color| {
|
||||
window.rect(x, y, w, h, orbclient::Color { data: color.0 });
|
||||
},
|
||||
);
|
||||
|
||||
window.sync();
|
||||
|
||||
|
|
|
|||
170
src/buffer.rs
170
src/buffer.rs
|
|
@ -365,10 +365,9 @@ impl Buffer {
|
|||
while self.scroll.layout < 0 {
|
||||
if self.scroll.line > 0 {
|
||||
self.scroll.line -= 1;
|
||||
let layout = self
|
||||
.line_layout(font_system, self.scroll.line)
|
||||
.expect("shape_until_scroll invalid scroll.line");
|
||||
self.scroll.layout += layout.len() as i32;
|
||||
if let Some(layout) = self.line_layout(font_system, self.scroll.line) {
|
||||
self.scroll.layout += layout.len() as i32;
|
||||
}
|
||||
} else {
|
||||
self.scroll.layout = 0;
|
||||
break;
|
||||
|
|
@ -801,8 +800,9 @@ impl Buffer {
|
|||
&mut self,
|
||||
font_system: &mut FontSystem,
|
||||
mut cursor: Cursor,
|
||||
mut cursor_x_opt: Option<i32>,
|
||||
motion: Motion,
|
||||
) -> Option<Cursor> {
|
||||
) -> Option<(Cursor, Option<i32>)> {
|
||||
match motion {
|
||||
Motion::LayoutCursor(layout_cursor) => {
|
||||
let layout = self.line_layout(font_system, layout_cursor.line)?;
|
||||
|
|
@ -811,7 +811,9 @@ impl Buffer {
|
|||
Some(some) => some,
|
||||
None => match layout.last() {
|
||||
Some(some) => some,
|
||||
None => return None,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -853,7 +855,7 @@ impl Buffer {
|
|||
cursor.index = self.lines.get(cursor.line)?.text().len();
|
||||
cursor.affinity = Affinity::After;
|
||||
}
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::Next => {
|
||||
let line = self.lines.get(cursor.line)?;
|
||||
|
|
@ -870,7 +872,7 @@ impl Buffer {
|
|||
cursor.index = 0;
|
||||
cursor.affinity = Affinity::Before;
|
||||
}
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::Left => {
|
||||
let rtl_opt = self
|
||||
|
|
@ -878,9 +880,15 @@ impl Buffer {
|
|||
.map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::Next)?;
|
||||
(cursor, cursor_x_opt) =
|
||||
self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Next)?;
|
||||
} else {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::Previous)?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::Previous,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -890,17 +898,23 @@ impl Buffer {
|
|||
.map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::Previous)?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::Previous,
|
||||
)?;
|
||||
} else {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::Next)?;
|
||||
(cursor, cursor_x_opt) =
|
||||
self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Next)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Motion::Up => {
|
||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||
|
||||
if cursor.x_opt.is_none() {
|
||||
cursor.x_opt = Some(
|
||||
if cursor_x_opt.is_none() {
|
||||
cursor_x_opt = Some(
|
||||
layout_cursor.glyph as i32, //TODO: glyph x position
|
||||
);
|
||||
}
|
||||
|
|
@ -912,20 +926,24 @@ impl Buffer {
|
|||
layout_cursor.layout = usize::max_value();
|
||||
}
|
||||
|
||||
if let Some(cursor_x) = cursor.x_opt {
|
||||
if let Some(cursor_x) = cursor_x_opt {
|
||||
layout_cursor.glyph = cursor_x as usize; //TODO: glyph x position
|
||||
}
|
||||
|
||||
cursor =
|
||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::LayoutCursor(layout_cursor),
|
||||
)?;
|
||||
}
|
||||
Motion::Down => {
|
||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||
|
||||
let layout_len = self.line_layout(font_system, layout_cursor.line)?.len();
|
||||
|
||||
if cursor.x_opt.is_none() {
|
||||
cursor.x_opt = Some(
|
||||
if cursor_x_opt.is_none() {
|
||||
cursor_x_opt = Some(
|
||||
layout_cursor.glyph as i32, //TODO: glyph x position
|
||||
);
|
||||
}
|
||||
|
|
@ -937,19 +955,30 @@ impl Buffer {
|
|||
layout_cursor.layout = 0;
|
||||
}
|
||||
|
||||
if let Some(cursor_x) = cursor.x_opt {
|
||||
if let Some(cursor_x) = cursor_x_opt {
|
||||
layout_cursor.glyph = cursor_x as usize; //TODO: glyph x position
|
||||
}
|
||||
|
||||
cursor =
|
||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::LayoutCursor(layout_cursor),
|
||||
)?;
|
||||
}
|
||||
Motion::Home => {
|
||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||
layout_cursor.glyph = 0;
|
||||
cursor =
|
||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
||||
cursor.x_opt = None;
|
||||
#[allow(unused_assignments)]
|
||||
{
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::LayoutCursor(layout_cursor),
|
||||
)?;
|
||||
}
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::SoftHome => {
|
||||
let line = self.lines.get(cursor.line)?;
|
||||
|
|
@ -959,34 +988,43 @@ impl Buffer {
|
|||
.filter_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) })
|
||||
.next()
|
||||
.unwrap_or(0);
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::End => {
|
||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||
layout_cursor.glyph = usize::max_value();
|
||||
cursor =
|
||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
||||
cursor.x_opt = None;
|
||||
#[allow(unused_assignments)]
|
||||
{
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::LayoutCursor(layout_cursor),
|
||||
)?;
|
||||
}
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::ParagraphStart => {
|
||||
cursor.index = 0;
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::ParagraphEnd => {
|
||||
cursor.index = self.lines.get(cursor.line)?.text().len();
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::PageUp => {
|
||||
cursor = self.cursor_motion(
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::Vertical(-self.size().1 as i32),
|
||||
)?;
|
||||
}
|
||||
Motion::PageDown => {
|
||||
cursor = self.cursor_motion(
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::Vertical(self.size().1 as i32),
|
||||
)?;
|
||||
}
|
||||
|
|
@ -996,12 +1034,18 @@ impl Buffer {
|
|||
match lines.cmp(&0) {
|
||||
cmp::Ordering::Less => {
|
||||
for _ in 0..-lines {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::Up)?;
|
||||
(cursor, cursor_x_opt) =
|
||||
self.cursor_motion(font_system, cursor, cursor_x_opt, Motion::Up)?;
|
||||
}
|
||||
}
|
||||
cmp::Ordering::Greater => {
|
||||
for _ in 0..lines {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::Down)?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::Down,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
cmp::Ordering::Equal => {}
|
||||
|
|
@ -1021,7 +1065,7 @@ impl Buffer {
|
|||
cursor.line -= 1;
|
||||
cursor.index = self.lines.get(cursor.line)?.text().len();
|
||||
}
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::NextWord => {
|
||||
let line = self.lines.get(cursor.line)?;
|
||||
|
|
@ -1036,7 +1080,7 @@ impl Buffer {
|
|||
cursor.line += 1;
|
||||
cursor.index = 0;
|
||||
}
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::LeftWord => {
|
||||
let rtl_opt = self
|
||||
|
|
@ -1044,9 +1088,19 @@ impl Buffer {
|
|||
.map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::NextWord)?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::NextWord,
|
||||
)?;
|
||||
} else {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::PreviousWord)?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::PreviousWord,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1056,30 +1110,44 @@ impl Buffer {
|
|||
.map(|shape| shape.rtl);
|
||||
if let Some(rtl) = rtl_opt {
|
||||
if rtl {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::PreviousWord)?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::PreviousWord,
|
||||
)?;
|
||||
} else {
|
||||
cursor = self.cursor_motion(font_system, cursor, Motion::NextWord)?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::NextWord,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Motion::BufferStart => {
|
||||
cursor.line = 0;
|
||||
cursor.index = 0;
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::BufferEnd => {
|
||||
cursor.line = self.lines.len() - 1;
|
||||
cursor.index = self.lines.get(cursor.line)?.text().len();
|
||||
cursor.x_opt = None;
|
||||
cursor_x_opt = None;
|
||||
}
|
||||
Motion::GotoLine(line) => {
|
||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||
layout_cursor.line = line;
|
||||
cursor =
|
||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
||||
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||
font_system,
|
||||
cursor,
|
||||
cursor_x_opt,
|
||||
Motion::LayoutCursor(layout_cursor),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Some(cursor)
|
||||
Some((cursor, cursor_x_opt))
|
||||
}
|
||||
|
||||
/// Draw the buffer
|
||||
|
|
@ -1203,8 +1271,14 @@ impl<'a> BorrowedWithFontSystem<'a, Buffer> {
|
|||
}
|
||||
|
||||
/// Apply a [`Motion`] to a [`Cursor`]
|
||||
pub fn cursor_motion(&mut self, cursor: Cursor, motion: Motion) -> Option<Cursor> {
|
||||
self.inner.cursor_motion(self.font_system, cursor, motion)
|
||||
pub fn cursor_motion(
|
||||
&mut self,
|
||||
cursor: Cursor,
|
||||
cursor_x_opt: Option<i32>,
|
||||
motion: Motion,
|
||||
) -> Option<(Cursor, Option<i32>)> {
|
||||
self.inner
|
||||
.cursor_motion(self.font_system, cursor, cursor_x_opt, motion)
|
||||
}
|
||||
|
||||
/// Draw the buffer
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use crate::Color;
|
||||
|
||||
/// Current cursor location
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Cursor {
|
||||
|
|
@ -10,10 +8,6 @@ pub struct Cursor {
|
|||
/// Whether to associate the cursor with the run before it or the run after it if placed at the
|
||||
/// boundary between two runs
|
||||
pub affinity: Affinity,
|
||||
/// Cached X position used for up and down movement
|
||||
pub x_opt: Option<i32>,
|
||||
/// Cursor color
|
||||
pub color: Option<Color>,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
|
|
@ -28,18 +22,6 @@ impl Cursor {
|
|||
line,
|
||||
index,
|
||||
affinity,
|
||||
x_opt: None,
|
||||
color: None,
|
||||
}
|
||||
}
|
||||
/// Create a new cursor, specifying the color
|
||||
pub const fn new_with_color(line: usize, index: usize, color: Color) -> Self {
|
||||
Self {
|
||||
line,
|
||||
index,
|
||||
affinity: Affinity::Before,
|
||||
x_opt: None,
|
||||
color: Some(color),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +61,7 @@ impl Affinity {
|
|||
}
|
||||
|
||||
/// The position of a cursor within a [`Buffer`].
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct LayoutCursor {
|
||||
/// Index of [`BufferLine`] in [`Buffer::lines`]
|
||||
pub line: usize,
|
||||
|
|
@ -150,7 +132,7 @@ pub enum Motion {
|
|||
}
|
||||
|
||||
/// Scroll position in [`Buffer`]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Scroll {
|
||||
/// Index of [`BufferLine`] in [`Buffer::lines`]. This will be adjusted as needed if layout is
|
||||
/// out of bounds
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||
#[cfg(feature = "swash")]
|
||||
use crate::Color;
|
||||
use crate::{
|
||||
Action, AttrsList, Buffer, BufferLine, Change, ChangeItem, Cursor, Edit, FontSystem, Selection,
|
||||
Shaping,
|
||||
Action, AttrsList, BorrowedWithFontSystem, Buffer, BufferLine, Change, ChangeItem, Cursor,
|
||||
Edit, FontSystem, Selection, Shaping,
|
||||
};
|
||||
|
||||
/// A wrapper of [`Buffer`] for easy editing
|
||||
|
|
@ -17,6 +17,7 @@ use crate::{
|
|||
pub struct Editor {
|
||||
buffer: Buffer,
|
||||
cursor: Cursor,
|
||||
cursor_x_opt: Option<i32>,
|
||||
selection: Selection,
|
||||
cursor_moved: bool,
|
||||
auto_indent: bool,
|
||||
|
|
@ -30,6 +31,7 @@ impl Editor {
|
|||
Self {
|
||||
buffer,
|
||||
cursor: Cursor::default(),
|
||||
cursor_x_opt: None,
|
||||
selection: Selection::None,
|
||||
cursor_moved: false,
|
||||
auto_indent: false,
|
||||
|
|
@ -37,6 +39,179 @@ impl Editor {
|
|||
change: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
text_color: Color,
|
||||
cursor_color: Color,
|
||||
selection_color: Color,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let line_height = self.buffer.metrics().line_height;
|
||||
|
||||
for run in self.buffer.layout_runs() {
|
||||
let line_i = run.line_i;
|
||||
let line_y = run.line_y;
|
||||
let line_top = run.line_top;
|
||||
|
||||
let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32)> {
|
||||
if cursor.line == line_i {
|
||||
for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
|
||||
if cursor.index == glyph.start {
|
||||
return Some((glyph_i, 0.0));
|
||||
} else if cursor.index > glyph.start && cursor.index < glyph.end {
|
||||
// Guess x offset based on characters
|
||||
let mut before = 0;
|
||||
let mut total = 0;
|
||||
|
||||
let cluster = &run.text[glyph.start..glyph.end];
|
||||
for (i, _) in cluster.grapheme_indices(true) {
|
||||
if glyph.start + i < cursor.index {
|
||||
before += 1;
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
|
||||
let offset = glyph.w * (before as f32) / (total as f32);
|
||||
return Some((glyph_i, offset));
|
||||
}
|
||||
}
|
||||
match run.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
if cursor.index == glyph.end {
|
||||
return Some((run.glyphs.len(), 0.0));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Some((0, 0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
// Highlight selection (TODO: HIGHLIGHT COLOR!)
|
||||
if let Some((start, end)) = self.selection_bounds() {
|
||||
if line_i >= start.line && line_i <= end.line {
|
||||
let mut range_opt = None;
|
||||
for glyph in run.glyphs.iter() {
|
||||
// Guess x offset based on characters
|
||||
let cluster = &run.text[glyph.start..glyph.end];
|
||||
let total = cluster.grapheme_indices(true).count();
|
||||
let mut c_x = glyph.x;
|
||||
let c_w = glyph.w / total as f32;
|
||||
for (i, c) in cluster.grapheme_indices(true) {
|
||||
let c_start = glyph.start + i;
|
||||
let c_end = glyph.start + i + c.len();
|
||||
if (start.line != line_i || c_end > start.index)
|
||||
&& (end.line != line_i || c_start < end.index)
|
||||
{
|
||||
range_opt = match range_opt.take() {
|
||||
Some((min, max)) => Some((
|
||||
cmp::min(min, c_x as i32),
|
||||
cmp::max(max, (c_x + c_w) as i32),
|
||||
)),
|
||||
None => Some((c_x as i32, (c_x + c_w) as i32)),
|
||||
};
|
||||
} else if let Some((min, max)) = range_opt.take() {
|
||||
f(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
line_height as u32,
|
||||
selection_color,
|
||||
);
|
||||
}
|
||||
c_x += c_w;
|
||||
}
|
||||
}
|
||||
|
||||
if run.glyphs.is_empty() && end.line > line_i {
|
||||
// Highlight all of internal empty lines
|
||||
range_opt = Some((0, self.buffer.size().0 as i32));
|
||||
}
|
||||
|
||||
if let Some((mut min, mut max)) = range_opt.take() {
|
||||
if end.line > line_i {
|
||||
// Draw to end of line
|
||||
if run.rtl {
|
||||
min = 0;
|
||||
} else {
|
||||
max = self.buffer.size().0 as i32;
|
||||
}
|
||||
}
|
||||
f(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
line_height as u32,
|
||||
selection_color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursor
|
||||
if let Some((cursor_glyph, cursor_glyph_offset)) = cursor_glyph_opt(&self.cursor) {
|
||||
let x = match run.glyphs.get(cursor_glyph) {
|
||||
Some(glyph) => {
|
||||
// Start of detected glyph
|
||||
if glyph.level.is_rtl() {
|
||||
(glyph.x + glyph.w - cursor_glyph_offset) as i32
|
||||
} else {
|
||||
(glyph.x + cursor_glyph_offset) as i32
|
||||
}
|
||||
}
|
||||
None => match run.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
// End of last glyph
|
||||
if glyph.level.is_rtl() {
|
||||
glyph.x as i32
|
||||
} else {
|
||||
(glyph.x + glyph.w) as i32
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Start of empty line
|
||||
0
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
f(x, line_top as i32, 1, line_height as u32, cursor_color);
|
||||
}
|
||||
|
||||
for glyph in run.glyphs.iter() {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
|
||||
let glyph_color = match glyph.color_opt {
|
||||
Some(some) => some,
|
||||
None => text_color,
|
||||
};
|
||||
|
||||
cache.with_pixels(
|
||||
font_system,
|
||||
physical_glyph.cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(
|
||||
physical_glyph.x + x,
|
||||
line_y as i32 + physical_glyph.y + y,
|
||||
1,
|
||||
1,
|
||||
color,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edit for Editor {
|
||||
|
|
@ -342,10 +517,12 @@ impl Edit for Editor {
|
|||
|
||||
match action {
|
||||
Action::Motion(motion) => {
|
||||
if let Some(new_cursor) =
|
||||
self.buffer.cursor_motion(font_system, self.cursor, motion)
|
||||
if let Some((cursor, cursor_x_opt)) =
|
||||
self.buffer
|
||||
.cursor_motion(font_system, self.cursor, self.cursor_x_opt, motion)
|
||||
{
|
||||
self.cursor = new_cursor;
|
||||
self.cursor = cursor;
|
||||
self.cursor_x_opt = cursor_x_opt;
|
||||
}
|
||||
}
|
||||
Action::Escape => {
|
||||
|
|
@ -577,9 +754,7 @@ impl Edit for Editor {
|
|||
|
||||
if let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
|
||||
if new_cursor != self.cursor {
|
||||
let color = self.cursor.color;
|
||||
self.cursor = new_cursor;
|
||||
self.cursor.color = color;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -589,9 +764,7 @@ impl Edit for Editor {
|
|||
|
||||
if let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
|
||||
if new_cursor != self.cursor {
|
||||
let color = self.cursor.color;
|
||||
self.cursor = new_cursor;
|
||||
self.cursor.color = color;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
self.selection = Selection::Word(self.cursor);
|
||||
|
|
@ -603,9 +776,7 @@ impl Edit for Editor {
|
|||
|
||||
if let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
|
||||
if new_cursor != self.cursor {
|
||||
let color = self.cursor.color;
|
||||
self.cursor = new_cursor;
|
||||
self.cursor.color = color;
|
||||
}
|
||||
self.selection = Selection::Line(self.cursor);
|
||||
self.buffer.set_redraw(true);
|
||||
|
|
@ -619,9 +790,7 @@ impl Edit for Editor {
|
|||
|
||||
if let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
|
||||
if new_cursor != self.cursor {
|
||||
let color = self.cursor.color;
|
||||
self.cursor = new_cursor;
|
||||
self.cursor.color = color;
|
||||
self.buffer.set_redraw(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -654,181 +823,27 @@ impl Edit for Editor {
|
|||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the editor
|
||||
impl<'a> BorrowedWithFontSystem<'a, Editor> {
|
||||
#[cfg(feature = "swash")]
|
||||
fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
pub fn draw<F>(
|
||||
&mut self,
|
||||
cache: &mut crate::SwashCache,
|
||||
color: Color,
|
||||
mut f: F,
|
||||
text_color: Color,
|
||||
cursor_color: Color,
|
||||
selection_color: Color,
|
||||
f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let line_height = self.buffer.metrics().line_height;
|
||||
|
||||
for run in self.buffer.layout_runs() {
|
||||
let line_i = run.line_i;
|
||||
let line_y = run.line_y;
|
||||
let line_top = run.line_top;
|
||||
|
||||
let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32)> {
|
||||
if cursor.line == line_i {
|
||||
for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
|
||||
if cursor.index == glyph.start {
|
||||
return Some((glyph_i, 0.0));
|
||||
} else if cursor.index > glyph.start && cursor.index < glyph.end {
|
||||
// Guess x offset based on characters
|
||||
let mut before = 0;
|
||||
let mut total = 0;
|
||||
|
||||
let cluster = &run.text[glyph.start..glyph.end];
|
||||
for (i, _) in cluster.grapheme_indices(true) {
|
||||
if glyph.start + i < cursor.index {
|
||||
before += 1;
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
|
||||
let offset = glyph.w * (before as f32) / (total as f32);
|
||||
return Some((glyph_i, offset));
|
||||
}
|
||||
}
|
||||
match run.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
if cursor.index == glyph.end {
|
||||
return Some((run.glyphs.len(), 0.0));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Some((0, 0.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
// Highlight selection (TODO: HIGHLIGHT COLOR!)
|
||||
if let Some((start, end)) = self.selection_bounds() {
|
||||
if line_i >= start.line && line_i <= end.line {
|
||||
let mut range_opt = None;
|
||||
for glyph in run.glyphs.iter() {
|
||||
// Guess x offset based on characters
|
||||
let cluster = &run.text[glyph.start..glyph.end];
|
||||
let total = cluster.grapheme_indices(true).count();
|
||||
let mut c_x = glyph.x;
|
||||
let c_w = glyph.w / total as f32;
|
||||
for (i, c) in cluster.grapheme_indices(true) {
|
||||
let c_start = glyph.start + i;
|
||||
let c_end = glyph.start + i + c.len();
|
||||
if (start.line != line_i || c_end > start.index)
|
||||
&& (end.line != line_i || c_start < end.index)
|
||||
{
|
||||
range_opt = match range_opt.take() {
|
||||
Some((min, max)) => Some((
|
||||
cmp::min(min, c_x as i32),
|
||||
cmp::max(max, (c_x + c_w) as i32),
|
||||
)),
|
||||
None => Some((c_x as i32, (c_x + c_w) as i32)),
|
||||
};
|
||||
} else if let Some((min, max)) = range_opt.take() {
|
||||
f(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
line_height as u32,
|
||||
Color::rgba(color.r(), color.g(), color.b(), 0x33),
|
||||
);
|
||||
}
|
||||
c_x += c_w;
|
||||
}
|
||||
}
|
||||
|
||||
if run.glyphs.is_empty() && end.line > line_i {
|
||||
// Highlight all of internal empty lines
|
||||
range_opt = Some((0, self.buffer.size().0 as i32));
|
||||
}
|
||||
|
||||
if let Some((mut min, mut max)) = range_opt.take() {
|
||||
if end.line > line_i {
|
||||
// Draw to end of line
|
||||
if run.rtl {
|
||||
min = 0;
|
||||
} else {
|
||||
max = self.buffer.size().0 as i32;
|
||||
}
|
||||
}
|
||||
f(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
line_height as u32,
|
||||
Color::rgba(color.r(), color.g(), color.b(), 0x33),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursor
|
||||
if let Some((cursor_glyph, cursor_glyph_offset)) = cursor_glyph_opt(&self.cursor) {
|
||||
let x = match run.glyphs.get(cursor_glyph) {
|
||||
Some(glyph) => {
|
||||
// Start of detected glyph
|
||||
if glyph.level.is_rtl() {
|
||||
(glyph.x + glyph.w - cursor_glyph_offset) as i32
|
||||
} else {
|
||||
(glyph.x + cursor_glyph_offset) as i32
|
||||
}
|
||||
}
|
||||
None => match run.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
// End of last glyph
|
||||
if glyph.level.is_rtl() {
|
||||
glyph.x as i32
|
||||
} else {
|
||||
(glyph.x + glyph.w) as i32
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Start of empty line
|
||||
0
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
f(
|
||||
x,
|
||||
line_top as i32,
|
||||
1,
|
||||
line_height as u32,
|
||||
self.cursor.color.unwrap_or(color),
|
||||
);
|
||||
}
|
||||
|
||||
for glyph in run.glyphs.iter() {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
|
||||
let glyph_color = match glyph.color_opt {
|
||||
Some(some) => some,
|
||||
None => color,
|
||||
};
|
||||
|
||||
cache.with_pixels(
|
||||
font_system,
|
||||
physical_glyph.cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(
|
||||
physical_glyph.x + x,
|
||||
line_y as i32 + physical_glyph.y + y,
|
||||
1,
|
||||
1,
|
||||
color,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
self.inner.draw(
|
||||
self.font_system,
|
||||
cache,
|
||||
text_color,
|
||||
cursor_color,
|
||||
selection_color,
|
||||
f,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ use alloc::{string::String, vec::Vec};
|
|||
use core::cmp;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
use crate::Color;
|
||||
use crate::{AttrsList, BorrowedWithFontSystem, Buffer, Cursor, FontSystem, Motion};
|
||||
|
||||
pub use self::editor::*;
|
||||
|
|
@ -265,17 +263,6 @@ pub trait Edit {
|
|||
|
||||
/// Perform an [Action] on the editor
|
||||
fn action(&mut self, font_system: &mut FontSystem, action: Action);
|
||||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
color: Color,
|
||||
f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color);
|
||||
}
|
||||
|
||||
impl<'a, T: Edit> BorrowedWithFontSystem<'a, T> {
|
||||
|
|
@ -296,13 +283,4 @@ impl<'a, T: Edit> BorrowedWithFontSystem<'a, T> {
|
|||
pub fn action(&mut self, action: Action) {
|
||||
self.inner.action(self.font_system, action);
|
||||
}
|
||||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, color: Color, f: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
self.inner.draw(self.font_system, cache, color, f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,10 +159,52 @@ impl<'a> SyntaxEditor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the default cursor color
|
||||
pub fn cursor_color(&self) -> Color {
|
||||
if let Some(some) = self.theme.settings.caret {
|
||||
Color::rgba(some.r, some.g, some.b, some.a)
|
||||
} else {
|
||||
self.foreground_color()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the default selection color
|
||||
pub fn selection_color(&self) -> Color {
|
||||
if let Some(some) = self.theme.settings.selection {
|
||||
Color::rgba(some.r, some.g, some.b, some.a)
|
||||
} else {
|
||||
let foreground_color = self.foreground_color();
|
||||
Color::rgba(
|
||||
foreground_color.r(),
|
||||
foreground_color.g(),
|
||||
foreground_color.b(),
|
||||
0x33,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current syntect theme
|
||||
pub fn theme(&self) -> &SyntaxTheme {
|
||||
self.theme
|
||||
}
|
||||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&self, font_system: &mut FontSystem, cache: &mut crate::SwashCache, mut f: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let size = self.buffer().size();
|
||||
f(0, 0, size.0 as u32, size.1 as u32, self.background_color());
|
||||
self.editor.draw(
|
||||
font_system,
|
||||
cache,
|
||||
self.foreground_color(),
|
||||
self.cursor_color(),
|
||||
self.selection_color(),
|
||||
f,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Edit for SyntaxEditor<'a> {
|
||||
|
|
@ -357,23 +399,6 @@ impl<'a> Edit for SyntaxEditor<'a> {
|
|||
fn action(&mut self, font_system: &mut FontSystem, action: Action) {
|
||||
self.editor.action(font_system, action);
|
||||
}
|
||||
|
||||
/// Draw the editor
|
||||
#[cfg(feature = "swash")]
|
||||
fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
_color: Color,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let size = self.buffer().size();
|
||||
f(0, 0, size.0 as u32, size.1 as u32, self.background_color());
|
||||
self.editor
|
||||
.draw(font_system, cache, self.foreground_color(), f);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> BorrowedWithFontSystem<'b, SyntaxEditor<'a>> {
|
||||
|
|
@ -386,4 +411,12 @@ impl<'a, 'b> BorrowedWithFontSystem<'b, SyntaxEditor<'a>> {
|
|||
pub fn load_text<P: AsRef<Path>>(&mut self, path: P, attrs: crate::Attrs) -> io::Result<()> {
|
||||
self.inner.load_text(self.font_system, path, attrs)
|
||||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, f: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
self.inner.draw(self.font_system, cache, f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
438
src/edit/vi.rs
438
src/edit/vi.rs
|
|
@ -201,6 +201,16 @@ impl<'a> ViEditor<'a> {
|
|||
self.editor.foreground_color()
|
||||
}
|
||||
|
||||
/// Get the default cursor color
|
||||
pub fn cursor_color(&self) -> Color {
|
||||
self.editor.cursor_color()
|
||||
}
|
||||
|
||||
/// Get the default selection color
|
||||
pub fn selection_color(&self) -> Color {
|
||||
self.editor.selection_color()
|
||||
}
|
||||
|
||||
/// Get the current syntect theme
|
||||
pub fn theme(&self) -> &SyntaxTheme {
|
||||
self.editor.theme()
|
||||
|
|
@ -248,6 +258,218 @@ impl<'a> ViEditor<'a> {
|
|||
self.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&self, font_system: &mut FontSystem, cache: &mut crate::SwashCache, mut f: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let size = self.buffer().size();
|
||||
f(0, 0, size.0 as u32, size.1 as u32, self.background_color());
|
||||
|
||||
let font_size = self.buffer().metrics().font_size;
|
||||
let line_height = self.buffer().metrics().line_height;
|
||||
let foreground_color = self.foreground_color();
|
||||
let cursor_color = self.cursor_color();
|
||||
let selection_color = self.selection_color();
|
||||
|
||||
for run in self.buffer().layout_runs() {
|
||||
let line_i = run.line_i;
|
||||
let line_y = run.line_y;
|
||||
let line_top = run.line_top;
|
||||
|
||||
let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32, f32)> {
|
||||
//TODO: better calculation of width
|
||||
let default_width = font_size / 2.0;
|
||||
if cursor.line == line_i {
|
||||
for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
|
||||
if cursor.index >= glyph.start && cursor.index < glyph.end {
|
||||
// Guess x offset based on characters
|
||||
let mut before = 0;
|
||||
let mut total = 0;
|
||||
|
||||
let cluster = &run.text[glyph.start..glyph.end];
|
||||
for (i, _) in cluster.grapheme_indices(true) {
|
||||
if glyph.start + i < cursor.index {
|
||||
before += 1;
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
|
||||
let width = glyph.w / (total as f32);
|
||||
let offset = (before as f32) * width;
|
||||
return Some((glyph_i, offset, width));
|
||||
}
|
||||
}
|
||||
match run.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
if cursor.index == glyph.end {
|
||||
return Some((run.glyphs.len(), 0.0, default_width));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Some((0, 0.0, default_width));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
// Highlight selection (TODO: HIGHLIGHT COLOR!)
|
||||
if let Some((start, end)) = self.selection_bounds() {
|
||||
if line_i >= start.line && line_i <= end.line {
|
||||
let mut range_opt = None;
|
||||
for glyph in run.glyphs.iter() {
|
||||
// Guess x offset based on characters
|
||||
let cluster = &run.text[glyph.start..glyph.end];
|
||||
let total = cluster.grapheme_indices(true).count();
|
||||
let mut c_x = glyph.x;
|
||||
let c_w = glyph.w / total as f32;
|
||||
for (i, c) in cluster.grapheme_indices(true) {
|
||||
let c_start = glyph.start + i;
|
||||
let c_end = glyph.start + i + c.len();
|
||||
if (start.line != line_i || c_end > start.index)
|
||||
&& (end.line != line_i || c_start < end.index)
|
||||
{
|
||||
range_opt = match range_opt.take() {
|
||||
Some((min, max)) => Some((
|
||||
cmp::min(min, c_x as i32),
|
||||
cmp::max(max, (c_x + c_w) as i32),
|
||||
)),
|
||||
None => Some((c_x as i32, (c_x + c_w) as i32)),
|
||||
};
|
||||
} else if let Some((min, max)) = range_opt.take() {
|
||||
f(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
line_height as u32,
|
||||
selection_color,
|
||||
);
|
||||
}
|
||||
c_x += c_w;
|
||||
}
|
||||
}
|
||||
|
||||
if run.glyphs.is_empty() && end.line > line_i {
|
||||
// Highlight all of internal empty lines
|
||||
range_opt = Some((0, self.buffer().size().0 as i32));
|
||||
}
|
||||
|
||||
if let Some((mut min, mut max)) = range_opt.take() {
|
||||
if end.line > line_i {
|
||||
// Draw to end of line
|
||||
if run.rtl {
|
||||
min = 0;
|
||||
} else {
|
||||
max = self.buffer().size().0 as i32;
|
||||
}
|
||||
}
|
||||
f(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
line_height as u32,
|
||||
selection_color,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursor
|
||||
if let Some((cursor_glyph, cursor_glyph_offset, cursor_glyph_width)) =
|
||||
cursor_glyph_opt(&self.cursor())
|
||||
{
|
||||
let block_cursor = if self.passthrough {
|
||||
false
|
||||
} else {
|
||||
match self.parser.mode {
|
||||
ViMode::Insert | ViMode::Replace => false,
|
||||
_ => true, /*TODO: determine block cursor in other modes*/
|
||||
}
|
||||
};
|
||||
|
||||
let (start_x, end_x) = match run.glyphs.get(cursor_glyph) {
|
||||
Some(glyph) => {
|
||||
// Start of detected glyph
|
||||
if glyph.level.is_rtl() {
|
||||
(
|
||||
(glyph.x + glyph.w - cursor_glyph_offset) as i32,
|
||||
(glyph.x + glyph.w - cursor_glyph_offset - cursor_glyph_width)
|
||||
as i32,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
(glyph.x + cursor_glyph_offset) as i32,
|
||||
(glyph.x + cursor_glyph_offset + cursor_glyph_width) as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
None => match run.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
// End of last glyph
|
||||
if glyph.level.is_rtl() {
|
||||
(glyph.x as i32, (glyph.x - cursor_glyph_width) as i32)
|
||||
} else {
|
||||
(
|
||||
(glyph.x + glyph.w) as i32,
|
||||
(glyph.x + glyph.w + cursor_glyph_width) as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Start of empty line
|
||||
(0, cursor_glyph_width as i32)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if block_cursor {
|
||||
let left_x = cmp::min(start_x, end_x);
|
||||
let right_x = cmp::max(start_x, end_x);
|
||||
f(
|
||||
left_x,
|
||||
line_top as i32,
|
||||
(right_x - left_x) as u32,
|
||||
line_height as u32,
|
||||
selection_color,
|
||||
);
|
||||
} else {
|
||||
f(
|
||||
start_x,
|
||||
line_top as i32,
|
||||
1,
|
||||
line_height as u32,
|
||||
cursor_color,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for glyph in run.glyphs.iter() {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
|
||||
let glyph_color = match glyph.color_opt {
|
||||
Some(some) => some,
|
||||
None => foreground_color,
|
||||
};
|
||||
|
||||
cache.with_pixels(
|
||||
font_system,
|
||||
physical_glyph.cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(
|
||||
physical_glyph.x + x,
|
||||
line_y as i32 + physical_glyph.y + y,
|
||||
1,
|
||||
1,
|
||||
color,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Edit for ViEditor<'a> {
|
||||
|
|
@ -838,214 +1060,6 @@ impl<'a> Edit for ViEditor<'a> {
|
|||
editor.action(font_system, action);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
fn draw<F>(
|
||||
&self,
|
||||
font_system: &mut FontSystem,
|
||||
cache: &mut crate::SwashCache,
|
||||
color: Color,
|
||||
mut f: F,
|
||||
) where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
let size = self.buffer().size();
|
||||
f(0, 0, size.0 as u32, size.1 as u32, self.background_color());
|
||||
|
||||
let font_size = self.buffer().metrics().font_size;
|
||||
let line_height = self.buffer().metrics().line_height;
|
||||
|
||||
for run in self.buffer().layout_runs() {
|
||||
let line_i = run.line_i;
|
||||
let line_y = run.line_y;
|
||||
let line_top = run.line_top;
|
||||
|
||||
let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32, f32)> {
|
||||
//TODO: better calculation of width
|
||||
let default_width = font_size / 2.0;
|
||||
if cursor.line == line_i {
|
||||
for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
|
||||
if cursor.index >= glyph.start && cursor.index < glyph.end {
|
||||
// Guess x offset based on characters
|
||||
let mut before = 0;
|
||||
let mut total = 0;
|
||||
|
||||
let cluster = &run.text[glyph.start..glyph.end];
|
||||
for (i, _) in cluster.grapheme_indices(true) {
|
||||
if glyph.start + i < cursor.index {
|
||||
before += 1;
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
|
||||
let width = glyph.w / (total as f32);
|
||||
let offset = (before as f32) * width;
|
||||
return Some((glyph_i, offset, width));
|
||||
}
|
||||
}
|
||||
match run.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
if cursor.index == glyph.end {
|
||||
return Some((run.glyphs.len(), 0.0, default_width));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Some((0, 0.0, default_width));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
// Highlight selection (TODO: HIGHLIGHT COLOR!)
|
||||
if let Some((start, end)) = self.selection_bounds() {
|
||||
if line_i >= start.line && line_i <= end.line {
|
||||
let mut range_opt = None;
|
||||
for glyph in run.glyphs.iter() {
|
||||
// Guess x offset based on characters
|
||||
let cluster = &run.text[glyph.start..glyph.end];
|
||||
let total = cluster.grapheme_indices(true).count();
|
||||
let mut c_x = glyph.x;
|
||||
let c_w = glyph.w / total as f32;
|
||||
for (i, c) in cluster.grapheme_indices(true) {
|
||||
let c_start = glyph.start + i;
|
||||
let c_end = glyph.start + i + c.len();
|
||||
if (start.line != line_i || c_end > start.index)
|
||||
&& (end.line != line_i || c_start < end.index)
|
||||
{
|
||||
range_opt = match range_opt.take() {
|
||||
Some((min, max)) => Some((
|
||||
cmp::min(min, c_x as i32),
|
||||
cmp::max(max, (c_x + c_w) as i32),
|
||||
)),
|
||||
None => Some((c_x as i32, (c_x + c_w) as i32)),
|
||||
};
|
||||
} else if let Some((min, max)) = range_opt.take() {
|
||||
f(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
line_height as u32,
|
||||
Color::rgba(color.r(), color.g(), color.b(), 0x33),
|
||||
);
|
||||
}
|
||||
c_x += c_w;
|
||||
}
|
||||
}
|
||||
|
||||
if run.glyphs.is_empty() && end.line > line_i {
|
||||
// Highlight all of internal empty lines
|
||||
range_opt = Some((0, self.buffer().size().0 as i32));
|
||||
}
|
||||
|
||||
if let Some((mut min, mut max)) = range_opt.take() {
|
||||
if end.line > line_i {
|
||||
// Draw to end of line
|
||||
if run.rtl {
|
||||
min = 0;
|
||||
} else {
|
||||
max = self.buffer().size().0 as i32;
|
||||
}
|
||||
}
|
||||
f(
|
||||
min,
|
||||
line_top as i32,
|
||||
cmp::max(0, max - min) as u32,
|
||||
line_height as u32,
|
||||
Color::rgba(color.r(), color.g(), color.b(), 0x33),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursor
|
||||
if let Some((cursor_glyph, cursor_glyph_offset, cursor_glyph_width)) =
|
||||
cursor_glyph_opt(&self.cursor())
|
||||
{
|
||||
let block_cursor = if self.passthrough {
|
||||
false
|
||||
} else {
|
||||
match self.parser.mode {
|
||||
ViMode::Insert | ViMode::Replace => false,
|
||||
_ => true, /*TODO: determine block cursor in other modes*/
|
||||
}
|
||||
};
|
||||
|
||||
let (start_x, end_x) = match run.glyphs.get(cursor_glyph) {
|
||||
Some(glyph) => {
|
||||
// Start of detected glyph
|
||||
if glyph.level.is_rtl() {
|
||||
(
|
||||
(glyph.x + glyph.w - cursor_glyph_offset) as i32,
|
||||
(glyph.x + glyph.w - cursor_glyph_offset - cursor_glyph_width)
|
||||
as i32,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
(glyph.x + cursor_glyph_offset) as i32,
|
||||
(glyph.x + cursor_glyph_offset + cursor_glyph_width) as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
None => match run.glyphs.last() {
|
||||
Some(glyph) => {
|
||||
// End of last glyph
|
||||
if glyph.level.is_rtl() {
|
||||
(glyph.x as i32, (glyph.x - cursor_glyph_width) as i32)
|
||||
} else {
|
||||
(
|
||||
(glyph.x + glyph.w) as i32,
|
||||
(glyph.x + glyph.w + cursor_glyph_width) as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Start of empty line
|
||||
(0, cursor_glyph_width as i32)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if block_cursor {
|
||||
let left_x = cmp::min(start_x, end_x);
|
||||
let right_x = cmp::max(start_x, end_x);
|
||||
f(
|
||||
left_x,
|
||||
line_top as i32,
|
||||
(right_x - left_x) as u32,
|
||||
line_height as u32,
|
||||
Color::rgba(color.r(), color.g(), color.b(), 0x33),
|
||||
);
|
||||
} else {
|
||||
f(start_x, line_top as i32, 1, line_height as u32, color);
|
||||
}
|
||||
}
|
||||
|
||||
for glyph in run.glyphs.iter() {
|
||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||
|
||||
let glyph_color = match glyph.color_opt {
|
||||
Some(some) => some,
|
||||
None => color,
|
||||
};
|
||||
|
||||
cache.with_pixels(
|
||||
font_system,
|
||||
physical_glyph.cache_key,
|
||||
glyph_color,
|
||||
|x, y, color| {
|
||||
f(
|
||||
physical_glyph.x + x,
|
||||
line_y as i32 + physical_glyph.y + y,
|
||||
1,
|
||||
1,
|
||||
color,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> BorrowedWithFontSystem<'b, ViEditor<'a>> {
|
||||
|
|
@ -1058,4 +1072,12 @@ impl<'a, 'b> BorrowedWithFontSystem<'b, ViEditor<'a>> {
|
|||
) -> std::io::Result<()> {
|
||||
self.inner.load_text(self.font_system, path, attrs)
|
||||
}
|
||||
|
||||
#[cfg(feature = "swash")]
|
||||
pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, f: F)
|
||||
where
|
||||
F: FnMut(i32, i32, u32, u32, Color),
|
||||
{
|
||||
self.inner.draw(self.font_system, cache, f);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue