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
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmic-text = { path = "../../", features = ["syntect"] }
|
cosmic-text = { path = "../../", features = ["syntect", "vi"] }
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
fontdb = "0.13"
|
fontdb = "0.13"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
|
@ -26,4 +26,4 @@ version = "0.11"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
vi = ["cosmic-text/vi"]
|
vi = []
|
||||||
|
|
|
||||||
|
|
@ -86,9 +86,6 @@ pub struct Window {
|
||||||
path_opt: Option<PathBuf>,
|
path_opt: Option<PathBuf>,
|
||||||
attrs: Attrs<'static>,
|
attrs: Attrs<'static>,
|
||||||
font_size: FontSize,
|
font_size: FontSize,
|
||||||
#[cfg(not(feature = "vi"))]
|
|
||||||
editor: Mutex<SyntaxEditor<'static>>,
|
|
||||||
#[cfg(feature = "vi")]
|
|
||||||
editor: Mutex<cosmic_text::ViEditor<'static>>,
|
editor: Mutex<cosmic_text::ViEditor<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +130,7 @@ impl Application for Window {
|
||||||
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
|
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
|
||||||
let attrs = cosmic_text::Attrs::new().family(cosmic_text::Family::Monospace);
|
let attrs = cosmic_text::Attrs::new().family(cosmic_text::Family::Monospace);
|
||||||
|
|
||||||
let mut editor = SyntaxEditor::new(
|
let editor = SyntaxEditor::new(
|
||||||
Buffer::new(
|
Buffer::new(
|
||||||
&mut FONT_SYSTEM.lock().unwrap(),
|
&mut FONT_SYSTEM.lock().unwrap(),
|
||||||
FontSize::Body.to_metrics(),
|
FontSize::Body.to_metrics(),
|
||||||
|
|
@ -143,8 +140,8 @@ impl Application for Window {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
#[cfg(feature = "vi")]
|
|
||||||
let mut editor = cosmic_text::ViEditor::new(editor);
|
let mut editor = cosmic_text::ViEditor::new(editor);
|
||||||
|
editor.set_passthrough(cfg!(feature = "vi"));
|
||||||
|
|
||||||
update_attrs(&mut editor, attrs);
|
update_attrs(&mut editor, attrs);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,13 @@ use cosmic::{
|
||||||
iced_runtime::keyboard::KeyCode,
|
iced_runtime::keyboard::KeyCode,
|
||||||
theme::{Theme, ThemeType},
|
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 std::{cmp, sync::Mutex, time::Instant};
|
||||||
|
|
||||||
use crate::FONT_SYSTEM;
|
use crate::FONT_SYSTEM;
|
||||||
|
|
||||||
pub struct Appearance {
|
pub struct Appearance {
|
||||||
background_color: Option<Color>,
|
background_color: Option<Color>,
|
||||||
text_color: Color,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait StyleSheet {
|
pub trait StyleSheet {
|
||||||
|
|
@ -24,23 +23,21 @@ impl StyleSheet for Theme {
|
||||||
match self.theme_type {
|
match self.theme_type {
|
||||||
ThemeType::Dark | ThemeType::HighContrastDark | ThemeType::Custom(_) => Appearance {
|
ThemeType::Dark | ThemeType::HighContrastDark | ThemeType::Custom(_) => Appearance {
|
||||||
background_color: Some(Color::from_rgb8(0x34, 0x34, 0x34)),
|
background_color: Some(Color::from_rgb8(0x34, 0x34, 0x34)),
|
||||||
text_color: Color::from_rgb8(0xFF, 0xFF, 0xFF),
|
|
||||||
},
|
},
|
||||||
ThemeType::Light | ThemeType::HighContrastLight => Appearance {
|
ThemeType::Light | ThemeType::HighContrastLight => Appearance {
|
||||||
background_color: Some(Color::from_rgb8(0xFC, 0xFC, 0xFC)),
|
background_color: Some(Color::from_rgb8(0xFC, 0xFC, 0xFC)),
|
||||||
text_color: Color::from_rgb8(0x00, 0x00, 0x00),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TextBox<'a, Editor> {
|
pub struct TextBox<'a, 'editor> {
|
||||||
editor: &'a Mutex<Editor>,
|
editor: &'a Mutex<ViEditor<'editor>>,
|
||||||
padding: Padding,
|
padding: Padding,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Editor> TextBox<'a, Editor> {
|
impl<'a, 'editor> TextBox<'a, 'editor> {
|
||||||
pub fn new(editor: &'a Mutex<Editor>) -> Self {
|
pub fn new(editor: &'a Mutex<ViEditor<'editor>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
editor,
|
editor,
|
||||||
padding: Padding::new(0.),
|
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)
|
TextBox::new(editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,11 +103,10 @@ fn draw_pixel(
|
||||||
buffer[offset + 3] = (current >> 24) as u8;
|
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
|
where
|
||||||
Renderer: cosmic::iced_core::Renderer + image::Renderer<Handle = image::Handle>,
|
Renderer: cosmic::iced_core::Renderer + image::Renderer<Handle = image::Handle>,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
Editor: Edit,
|
|
||||||
{
|
{
|
||||||
fn tag(&self) -> tree::Tag {
|
fn tag(&self) -> tree::Tag {
|
||||||
tree::Tag::of::<State>()
|
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 mut editor = self.editor.lock().unwrap();
|
||||||
|
|
||||||
let view_w = cmp::min(viewport.width as i32, layout.bounds().width as i32)
|
let view_w = cmp::min(viewport.width as i32, layout.bounds().width as i32)
|
||||||
|
|
@ -229,18 +218,14 @@ where
|
||||||
|
|
||||||
// Draw to pixel buffer
|
// Draw to pixel buffer
|
||||||
let mut pixels = vec![0; image_w as usize * image_h as usize * 4];
|
let mut pixels = vec![0; image_w as usize * image_h as usize * 4];
|
||||||
editor.draw(
|
editor.draw(&mut state.cache.lock().unwrap(), |x, y, w, h, color| {
|
||||||
&mut state.cache.lock().unwrap(),
|
//TODO: improve performance
|
||||||
text_color,
|
for row in 0..h as i32 {
|
||||||
|x, y, w, h, color| {
|
for col in 0..w as i32 {
|
||||||
//TODO: improve performance
|
draw_pixel(&mut pixels, image_w, image_h, x + col, y + row, color);
|
||||||
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
|
// Restore original metrics
|
||||||
editor.buffer_mut().set_metrics(metrics);
|
editor.buffer_mut().set_metrics(metrics);
|
||||||
|
|
@ -370,14 +355,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'editor, Editor, Message, Renderer> From<TextBox<'a, Editor>>
|
impl<'a, 'editor, Message, Renderer> From<TextBox<'a, 'editor>> for Element<'a, Message, Renderer>
|
||||||
for Element<'a, Message, Renderer>
|
|
||||||
where
|
where
|
||||||
Renderer: renderer::Renderer + image::Renderer<Handle = image::Handle>,
|
Renderer: renderer::Renderer + image::Renderer<Handle = image::Handle>,
|
||||||
Renderer::Theme: StyleSheet,
|
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)
|
Self::new(text_box)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,8 +95,7 @@ fn main() {
|
||||||
let bg = editor.background_color();
|
let bg = editor.background_color();
|
||||||
window.set(orbclient::Color::rgb(bg.r(), bg.g(), bg.b()));
|
window.set(orbclient::Color::rgb(bg.r(), bg.g(), bg.b()));
|
||||||
|
|
||||||
let fg = editor.foreground_color();
|
editor.draw(&mut swash_cache, |x, y, w, h, color| {
|
||||||
editor.draw(&mut swash_cache, fg, |x, y, w, h, color| {
|
|
||||||
window.rect(
|
window.rect(
|
||||||
line_x as i32 + x,
|
line_x as i32 + x,
|
||||||
y,
|
y,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ fn redraw(
|
||||||
) {
|
) {
|
||||||
let bg_color = orbclient::Color::rgb(0x34, 0x34, 0x34);
|
let bg_color = orbclient::Color::rgb(0x34, 0x34, 0x34);
|
||||||
let font_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
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);
|
editor.shape_as_needed(true);
|
||||||
if editor.buffer().redraw() {
|
if editor.buffer().redraw() {
|
||||||
|
|
@ -22,9 +24,15 @@ fn redraw(
|
||||||
|
|
||||||
window.set(bg_color);
|
window.set(bg_color);
|
||||||
|
|
||||||
editor.draw(swash_cache, font_color, |x, y, w, h, color| {
|
editor.draw(
|
||||||
window.rect(x, y, w, h, orbclient::Color { data: color.0 });
|
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();
|
window.sync();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,8 @@ fn main() {
|
||||||
loop {
|
loop {
|
||||||
let bg_color = orbclient::Color::rgb(0x34, 0x34, 0x34);
|
let bg_color = orbclient::Color::rgb(0x34, 0x34, 0x34);
|
||||||
let font_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
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);
|
editor.shape_as_needed(true);
|
||||||
if editor.buffer().redraw() {
|
if editor.buffer().redraw() {
|
||||||
|
|
@ -137,9 +139,15 @@ fn main() {
|
||||||
|
|
||||||
window.set(bg_color);
|
window.set(bg_color);
|
||||||
|
|
||||||
editor.draw(&mut swash_cache, font_color, |x, y, w, h, color| {
|
editor.draw(
|
||||||
window.rect(x, y, w, h, orbclient::Color { data: color.0 });
|
&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();
|
window.sync();
|
||||||
|
|
||||||
|
|
|
||||||
170
src/buffer.rs
170
src/buffer.rs
|
|
@ -365,10 +365,9 @@ impl Buffer {
|
||||||
while self.scroll.layout < 0 {
|
while self.scroll.layout < 0 {
|
||||||
if self.scroll.line > 0 {
|
if self.scroll.line > 0 {
|
||||||
self.scroll.line -= 1;
|
self.scroll.line -= 1;
|
||||||
let layout = self
|
if let Some(layout) = self.line_layout(font_system, self.scroll.line) {
|
||||||
.line_layout(font_system, self.scroll.line)
|
self.scroll.layout += layout.len() as i32;
|
||||||
.expect("shape_until_scroll invalid scroll.line");
|
}
|
||||||
self.scroll.layout += layout.len() as i32;
|
|
||||||
} else {
|
} else {
|
||||||
self.scroll.layout = 0;
|
self.scroll.layout = 0;
|
||||||
break;
|
break;
|
||||||
|
|
@ -801,8 +800,9 @@ impl Buffer {
|
||||||
&mut self,
|
&mut self,
|
||||||
font_system: &mut FontSystem,
|
font_system: &mut FontSystem,
|
||||||
mut cursor: Cursor,
|
mut cursor: Cursor,
|
||||||
|
mut cursor_x_opt: Option<i32>,
|
||||||
motion: Motion,
|
motion: Motion,
|
||||||
) -> Option<Cursor> {
|
) -> Option<(Cursor, Option<i32>)> {
|
||||||
match motion {
|
match motion {
|
||||||
Motion::LayoutCursor(layout_cursor) => {
|
Motion::LayoutCursor(layout_cursor) => {
|
||||||
let layout = self.line_layout(font_system, layout_cursor.line)?;
|
let layout = self.line_layout(font_system, layout_cursor.line)?;
|
||||||
|
|
@ -811,7 +811,9 @@ impl Buffer {
|
||||||
Some(some) => some,
|
Some(some) => some,
|
||||||
None => match layout.last() {
|
None => match layout.last() {
|
||||||
Some(some) => some,
|
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.index = self.lines.get(cursor.line)?.text().len();
|
||||||
cursor.affinity = Affinity::After;
|
cursor.affinity = Affinity::After;
|
||||||
}
|
}
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::Next => {
|
Motion::Next => {
|
||||||
let line = self.lines.get(cursor.line)?;
|
let line = self.lines.get(cursor.line)?;
|
||||||
|
|
@ -870,7 +872,7 @@ impl Buffer {
|
||||||
cursor.index = 0;
|
cursor.index = 0;
|
||||||
cursor.affinity = Affinity::Before;
|
cursor.affinity = Affinity::Before;
|
||||||
}
|
}
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::Left => {
|
Motion::Left => {
|
||||||
let rtl_opt = self
|
let rtl_opt = self
|
||||||
|
|
@ -878,9 +880,15 @@ impl Buffer {
|
||||||
.map(|shape| shape.rtl);
|
.map(|shape| shape.rtl);
|
||||||
if let Some(rtl) = rtl_opt {
|
if let Some(rtl) = rtl_opt {
|
||||||
if rtl {
|
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 {
|
} 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);
|
.map(|shape| shape.rtl);
|
||||||
if let Some(rtl) = rtl_opt {
|
if let Some(rtl) = rtl_opt {
|
||||||
if rtl {
|
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 {
|
} 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 => {
|
Motion::Up => {
|
||||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||||
|
|
||||||
if cursor.x_opt.is_none() {
|
if cursor_x_opt.is_none() {
|
||||||
cursor.x_opt = Some(
|
cursor_x_opt = Some(
|
||||||
layout_cursor.glyph as i32, //TODO: glyph x position
|
layout_cursor.glyph as i32, //TODO: glyph x position
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -912,20 +926,24 @@ impl Buffer {
|
||||||
layout_cursor.layout = usize::max_value();
|
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
|
layout_cursor.glyph = cursor_x as usize; //TODO: glyph x position
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor =
|
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
font_system,
|
||||||
|
cursor,
|
||||||
|
cursor_x_opt,
|
||||||
|
Motion::LayoutCursor(layout_cursor),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Motion::Down => {
|
Motion::Down => {
|
||||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||||
|
|
||||||
let layout_len = self.line_layout(font_system, layout_cursor.line)?.len();
|
let layout_len = self.line_layout(font_system, layout_cursor.line)?.len();
|
||||||
|
|
||||||
if cursor.x_opt.is_none() {
|
if cursor_x_opt.is_none() {
|
||||||
cursor.x_opt = Some(
|
cursor_x_opt = Some(
|
||||||
layout_cursor.glyph as i32, //TODO: glyph x position
|
layout_cursor.glyph as i32, //TODO: glyph x position
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -937,19 +955,30 @@ impl Buffer {
|
||||||
layout_cursor.layout = 0;
|
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
|
layout_cursor.glyph = cursor_x as usize; //TODO: glyph x position
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor =
|
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
font_system,
|
||||||
|
cursor,
|
||||||
|
cursor_x_opt,
|
||||||
|
Motion::LayoutCursor(layout_cursor),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Motion::Home => {
|
Motion::Home => {
|
||||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||||
layout_cursor.glyph = 0;
|
layout_cursor.glyph = 0;
|
||||||
cursor =
|
#[allow(unused_assignments)]
|
||||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
{
|
||||||
cursor.x_opt = None;
|
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||||
|
font_system,
|
||||||
|
cursor,
|
||||||
|
cursor_x_opt,
|
||||||
|
Motion::LayoutCursor(layout_cursor),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::SoftHome => {
|
Motion::SoftHome => {
|
||||||
let line = self.lines.get(cursor.line)?;
|
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) })
|
.filter_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) })
|
||||||
.next()
|
.next()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::End => {
|
Motion::End => {
|
||||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||||
layout_cursor.glyph = usize::max_value();
|
layout_cursor.glyph = usize::max_value();
|
||||||
cursor =
|
#[allow(unused_assignments)]
|
||||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
{
|
||||||
cursor.x_opt = None;
|
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||||
|
font_system,
|
||||||
|
cursor,
|
||||||
|
cursor_x_opt,
|
||||||
|
Motion::LayoutCursor(layout_cursor),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::ParagraphStart => {
|
Motion::ParagraphStart => {
|
||||||
cursor.index = 0;
|
cursor.index = 0;
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::ParagraphEnd => {
|
Motion::ParagraphEnd => {
|
||||||
cursor.index = self.lines.get(cursor.line)?.text().len();
|
cursor.index = self.lines.get(cursor.line)?.text().len();
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::PageUp => {
|
Motion::PageUp => {
|
||||||
cursor = self.cursor_motion(
|
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||||
font_system,
|
font_system,
|
||||||
cursor,
|
cursor,
|
||||||
|
cursor_x_opt,
|
||||||
Motion::Vertical(-self.size().1 as i32),
|
Motion::Vertical(-self.size().1 as i32),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Motion::PageDown => {
|
Motion::PageDown => {
|
||||||
cursor = self.cursor_motion(
|
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||||
font_system,
|
font_system,
|
||||||
cursor,
|
cursor,
|
||||||
|
cursor_x_opt,
|
||||||
Motion::Vertical(self.size().1 as i32),
|
Motion::Vertical(self.size().1 as i32),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
@ -996,12 +1034,18 @@ impl Buffer {
|
||||||
match lines.cmp(&0) {
|
match lines.cmp(&0) {
|
||||||
cmp::Ordering::Less => {
|
cmp::Ordering::Less => {
|
||||||
for _ in 0..-lines {
|
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 => {
|
cmp::Ordering::Greater => {
|
||||||
for _ in 0..lines {
|
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 => {}
|
cmp::Ordering::Equal => {}
|
||||||
|
|
@ -1021,7 +1065,7 @@ impl Buffer {
|
||||||
cursor.line -= 1;
|
cursor.line -= 1;
|
||||||
cursor.index = self.lines.get(cursor.line)?.text().len();
|
cursor.index = self.lines.get(cursor.line)?.text().len();
|
||||||
}
|
}
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::NextWord => {
|
Motion::NextWord => {
|
||||||
let line = self.lines.get(cursor.line)?;
|
let line = self.lines.get(cursor.line)?;
|
||||||
|
|
@ -1036,7 +1080,7 @@ impl Buffer {
|
||||||
cursor.line += 1;
|
cursor.line += 1;
|
||||||
cursor.index = 0;
|
cursor.index = 0;
|
||||||
}
|
}
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::LeftWord => {
|
Motion::LeftWord => {
|
||||||
let rtl_opt = self
|
let rtl_opt = self
|
||||||
|
|
@ -1044,9 +1088,19 @@ impl Buffer {
|
||||||
.map(|shape| shape.rtl);
|
.map(|shape| shape.rtl);
|
||||||
if let Some(rtl) = rtl_opt {
|
if let Some(rtl) = rtl_opt {
|
||||||
if rtl {
|
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 {
|
} 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);
|
.map(|shape| shape.rtl);
|
||||||
if let Some(rtl) = rtl_opt {
|
if let Some(rtl) = rtl_opt {
|
||||||
if rtl {
|
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 {
|
} 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 => {
|
Motion::BufferStart => {
|
||||||
cursor.line = 0;
|
cursor.line = 0;
|
||||||
cursor.index = 0;
|
cursor.index = 0;
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::BufferEnd => {
|
Motion::BufferEnd => {
|
||||||
cursor.line = self.lines.len() - 1;
|
cursor.line = self.lines.len() - 1;
|
||||||
cursor.index = self.lines.get(cursor.line)?.text().len();
|
cursor.index = self.lines.get(cursor.line)?.text().len();
|
||||||
cursor.x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
Motion::GotoLine(line) => {
|
Motion::GotoLine(line) => {
|
||||||
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
let mut layout_cursor = self.layout_cursor(font_system, cursor)?;
|
||||||
layout_cursor.line = line;
|
layout_cursor.line = line;
|
||||||
cursor =
|
(cursor, cursor_x_opt) = self.cursor_motion(
|
||||||
self.cursor_motion(font_system, cursor, Motion::LayoutCursor(layout_cursor))?;
|
font_system,
|
||||||
|
cursor,
|
||||||
|
cursor_x_opt,
|
||||||
|
Motion::LayoutCursor(layout_cursor),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(cursor)
|
Some((cursor, cursor_x_opt))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the buffer
|
/// Draw the buffer
|
||||||
|
|
@ -1203,8 +1271,14 @@ impl<'a> BorrowedWithFontSystem<'a, Buffer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a [`Motion`] to a [`Cursor`]
|
/// Apply a [`Motion`] to a [`Cursor`]
|
||||||
pub fn cursor_motion(&mut self, cursor: Cursor, motion: Motion) -> Option<Cursor> {
|
pub fn cursor_motion(
|
||||||
self.inner.cursor_motion(self.font_system, 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
|
/// Draw the buffer
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use crate::Color;
|
|
||||||
|
|
||||||
/// Current cursor location
|
/// Current cursor location
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub struct Cursor {
|
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
|
/// Whether to associate the cursor with the run before it or the run after it if placed at the
|
||||||
/// boundary between two runs
|
/// boundary between two runs
|
||||||
pub affinity: Affinity,
|
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 {
|
impl Cursor {
|
||||||
|
|
@ -28,18 +22,6 @@ impl Cursor {
|
||||||
line,
|
line,
|
||||||
index,
|
index,
|
||||||
affinity,
|
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`].
|
/// 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 {
|
pub struct LayoutCursor {
|
||||||
/// Index of [`BufferLine`] in [`Buffer::lines`]
|
/// Index of [`BufferLine`] in [`Buffer::lines`]
|
||||||
pub line: usize,
|
pub line: usize,
|
||||||
|
|
@ -150,7 +132,7 @@ pub enum Motion {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scroll position in [`Buffer`]
|
/// Scroll position in [`Buffer`]
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub struct Scroll {
|
pub struct Scroll {
|
||||||
/// Index of [`BufferLine`] in [`Buffer::lines`]. This will be adjusted as needed if layout is
|
/// Index of [`BufferLine`] in [`Buffer::lines`]. This will be adjusted as needed if layout is
|
||||||
/// out of bounds
|
/// out of bounds
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||||
#[cfg(feature = "swash")]
|
#[cfg(feature = "swash")]
|
||||||
use crate::Color;
|
use crate::Color;
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AttrsList, Buffer, BufferLine, Change, ChangeItem, Cursor, Edit, FontSystem, Selection,
|
Action, AttrsList, BorrowedWithFontSystem, Buffer, BufferLine, Change, ChangeItem, Cursor,
|
||||||
Shaping,
|
Edit, FontSystem, Selection, Shaping,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A wrapper of [`Buffer`] for easy editing
|
/// A wrapper of [`Buffer`] for easy editing
|
||||||
|
|
@ -17,6 +17,7 @@ use crate::{
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
|
cursor_x_opt: Option<i32>,
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
cursor_moved: bool,
|
cursor_moved: bool,
|
||||||
auto_indent: bool,
|
auto_indent: bool,
|
||||||
|
|
@ -30,6 +31,7 @@ impl Editor {
|
||||||
Self {
|
Self {
|
||||||
buffer,
|
buffer,
|
||||||
cursor: Cursor::default(),
|
cursor: Cursor::default(),
|
||||||
|
cursor_x_opt: None,
|
||||||
selection: Selection::None,
|
selection: Selection::None,
|
||||||
cursor_moved: false,
|
cursor_moved: false,
|
||||||
auto_indent: false,
|
auto_indent: false,
|
||||||
|
|
@ -37,6 +39,179 @@ impl Editor {
|
||||||
change: None,
|
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 {
|
impl Edit for Editor {
|
||||||
|
|
@ -342,10 +517,12 @@ impl Edit for Editor {
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
Action::Motion(motion) => {
|
Action::Motion(motion) => {
|
||||||
if let Some(new_cursor) =
|
if let Some((cursor, cursor_x_opt)) =
|
||||||
self.buffer.cursor_motion(font_system, self.cursor, motion)
|
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 => {
|
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 let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
|
||||||
if new_cursor != self.cursor {
|
if new_cursor != self.cursor {
|
||||||
let color = self.cursor.color;
|
|
||||||
self.cursor = new_cursor;
|
self.cursor = new_cursor;
|
||||||
self.cursor.color = color;
|
|
||||||
self.buffer.set_redraw(true);
|
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 let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
|
||||||
if new_cursor != self.cursor {
|
if new_cursor != self.cursor {
|
||||||
let color = self.cursor.color;
|
|
||||||
self.cursor = new_cursor;
|
self.cursor = new_cursor;
|
||||||
self.cursor.color = color;
|
|
||||||
self.buffer.set_redraw(true);
|
self.buffer.set_redraw(true);
|
||||||
}
|
}
|
||||||
self.selection = Selection::Word(self.cursor);
|
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 let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
|
||||||
if new_cursor != self.cursor {
|
if new_cursor != self.cursor {
|
||||||
let color = self.cursor.color;
|
|
||||||
self.cursor = new_cursor;
|
self.cursor = new_cursor;
|
||||||
self.cursor.color = color;
|
|
||||||
}
|
}
|
||||||
self.selection = Selection::Line(self.cursor);
|
self.selection = Selection::Line(self.cursor);
|
||||||
self.buffer.set_redraw(true);
|
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 let Some(new_cursor) = self.buffer.hit(x as f32, y as f32) {
|
||||||
if new_cursor != self.cursor {
|
if new_cursor != self.cursor {
|
||||||
let color = self.cursor.color;
|
|
||||||
self.cursor = new_cursor;
|
self.cursor = new_cursor;
|
||||||
self.cursor.color = color;
|
|
||||||
self.buffer.set_redraw(true);
|
self.buffer.set_redraw(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -654,181 +823,27 @@ impl Edit for Editor {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw the editor
|
impl<'a> BorrowedWithFontSystem<'a, Editor> {
|
||||||
#[cfg(feature = "swash")]
|
#[cfg(feature = "swash")]
|
||||||
fn draw<F>(
|
pub fn draw<F>(
|
||||||
&self,
|
&mut self,
|
||||||
font_system: &mut FontSystem,
|
|
||||||
cache: &mut crate::SwashCache,
|
cache: &mut crate::SwashCache,
|
||||||
color: Color,
|
text_color: Color,
|
||||||
mut f: F,
|
cursor_color: Color,
|
||||||
|
selection_color: Color,
|
||||||
|
f: F,
|
||||||
) where
|
) where
|
||||||
F: FnMut(i32, i32, u32, u32, Color),
|
F: FnMut(i32, i32, u32, u32, Color),
|
||||||
{
|
{
|
||||||
let line_height = self.buffer.metrics().line_height;
|
self.inner.draw(
|
||||||
|
self.font_system,
|
||||||
for run in self.buffer.layout_runs() {
|
cache,
|
||||||
let line_i = run.line_i;
|
text_color,
|
||||||
let line_y = run.line_y;
|
cursor_color,
|
||||||
let line_top = run.line_top;
|
selection_color,
|
||||||
|
f,
|
||||||
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,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ use alloc::{string::String, vec::Vec};
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
#[cfg(feature = "swash")]
|
|
||||||
use crate::Color;
|
|
||||||
use crate::{AttrsList, BorrowedWithFontSystem, Buffer, Cursor, FontSystem, Motion};
|
use crate::{AttrsList, BorrowedWithFontSystem, Buffer, Cursor, FontSystem, Motion};
|
||||||
|
|
||||||
pub use self::editor::*;
|
pub use self::editor::*;
|
||||||
|
|
@ -265,17 +263,6 @@ pub trait Edit {
|
||||||
|
|
||||||
/// Perform an [Action] on the editor
|
/// Perform an [Action] on the editor
|
||||||
fn action(&mut self, font_system: &mut FontSystem, action: Action);
|
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> {
|
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) {
|
pub fn action(&mut self, action: Action) {
|
||||||
self.inner.action(self.font_system, 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
|
/// Get the current syntect theme
|
||||||
pub fn theme(&self) -> &SyntaxTheme {
|
pub fn theme(&self) -> &SyntaxTheme {
|
||||||
self.theme
|
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> {
|
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) {
|
fn action(&mut self, font_system: &mut FontSystem, action: Action) {
|
||||||
self.editor.action(font_system, 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>> {
|
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<()> {
|
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)
|
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()
|
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
|
/// Get the current syntect theme
|
||||||
pub fn theme(&self) -> &SyntaxTheme {
|
pub fn theme(&self) -> &SyntaxTheme {
|
||||||
self.editor.theme()
|
self.editor.theme()
|
||||||
|
|
@ -248,6 +258,218 @@ impl<'a> ViEditor<'a> {
|
||||||
self.changed = true;
|
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> {
|
impl<'a> Edit for ViEditor<'a> {
|
||||||
|
|
@ -838,214 +1060,6 @@ impl<'a> Edit for ViEditor<'a> {
|
||||||
editor.action(font_system, action);
|
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>> {
|
impl<'a, 'b> BorrowedWithFontSystem<'b, ViEditor<'a>> {
|
||||||
|
|
@ -1058,4 +1072,12 @@ impl<'a, 'b> BorrowedWithFontSystem<'b, ViEditor<'a>> {
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
self.inner.load_text(self.font_system, path, attrs)
|
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