cosmic-text/examples/editor-libcosmic/src/text_box.rs
2022-10-18 13:05:36 -06:00

275 lines
8.8 KiB
Rust

use cosmic::iced_native::{
{Color, Element, Length, Point, Rectangle, Size, Shell},
clipboard::Clipboard,
event::{
Event,
Status,
},
keyboard::{Event as KeyEvent, KeyCode},
layout::{self, Layout},
renderer,
widget::{self, Widget},
};
use cosmic_text::{
FontLineIndex,
TextAction,
TextBuffer,
};
use std::{
sync::{Arc, RwLock},
time::Instant,
};
pub struct TextBox<'a> {
buffer: Arc<RwLock<TextBuffer<'a>>>,
}
impl<'a> TextBox<'a> {
pub fn new(buffer: Arc<RwLock<TextBuffer<'a>>>) -> Self {
Self { buffer }
}
}
pub fn text_box<'a>(buffer: Arc<RwLock<TextBuffer<'a>>>) -> TextBox<'a> {
TextBox::new(buffer)
}
impl<'a, Message, Renderer> Widget<Message, Renderer> for TextBox<'a>
where
Renderer: renderer::Renderer,
{
fn width(&self) -> Length {
Length::Shrink
}
fn height(&self) -> Length {
Length::Shrink
}
fn layout(
&self,
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
println!("{:?}", limits);
let size = limits.max();
{
let mut buffer = self.buffer.write().unwrap();
buffer.set_size(size.width as i32, size.height as i32);
}
layout::Node::new(size)
}
fn draw(
&self,
_state: &widget::Tree,
renderer: &mut Renderer,
_theme: &Renderer::Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor_position: Point,
_viewport: &Rectangle,
) {
let buffer = self.buffer.read().unwrap();
let font_size = buffer.font_size();
let line_height = buffer.line_height();
let instant = Instant::now();
renderer.fill_quad(
renderer::Quad {
bounds: layout.bounds(),
border_radius: 0.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
Color::from_rgb8(0x34, 0x34, 0x34),
);
let line_x = layout.bounds().x as i32;
let mut line_y = layout.bounds().y as i32 + line_height;
let mut start_line_opt = None;
let mut end_line = FontLineIndex::new(0);
for (line_i, line) in buffer
.layout_lines()
.iter()
.skip(buffer.scroll as usize)
.enumerate()
{
if line_y >= (layout.bounds().y + layout.bounds().height) as i32 {
break;
}
end_line = line.line_i;
if start_line_opt == None {
start_line_opt = Some(end_line);
}
if buffer.cursor.line == line_i + buffer.scroll as usize {
if buffer.cursor.glyph >= line.glyphs.len() {
let x = match line.glyphs.last() {
Some(glyph) => glyph.x + glyph.w,
None => 0.0,
};
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle::new(
Point::new(line_x as f32 + x, (line_y - font_size) as f32),
Size::new((font_size / 2) as f32, line_height as f32)
),
border_radius: 0.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
Color::from_rgba8(0xFF, 0xFF, 0xFF, 0.125),
);
} else {
let glyph = &line.glyphs[buffer.cursor.glyph];
renderer.fill_quad(
renderer::Quad {
bounds: Rectangle::new(
Point::new(line_x as f32 + glyph.x, (line_y - font_size) as f32),
Size::new(glyph.w, line_height as f32)
),
border_radius: 0.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
Color::from_rgba8(0xFF, 0xFF, 0xFF, 0.125),
);
let text_line = &buffer.text_lines()[line.line_i.get()];
log::info!(
"{}, {}: '{}' ('{}'): '{}'",
glyph.start,
glyph.end,
glyph.font.info.family,
glyph.font.info.post_script_name,
&text_line[glyph.start..glyph.end],
);
}
}
line.draw(0xFFFFFF, |x, y, data| {
let a = (data >> 24) as u8;
if a > 0 {
let r = (data >> 16) as u8;
let g = (data >> 8) as u8;
let b = data as u8;
let bounds = Rectangle::new(
Point::new((line_x + x) as f32, (line_y + y) as f32),
Size::new(1.0, 1.0)
);
let color = Color::from_rgba8(r, g, b, a as f32 / 255.0);
renderer.fill_quad(
renderer::Quad {
bounds,
border_radius: 0.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
color
);
}
});
line_y += line_height;
}
/*
// Draw scrollbar
{
let start_line = start_line_opt.unwrap_or(end_line);
let lines = buffer.text_lines().len();
let start_y = (start_line.get() * window.height() as usize) / lines;
let end_y = (end_line.get() * window.height() as usize) / lines;
if end_y > start_y {
window.rect(
window.width() as i32 - line_x as i32,
start_y as i32,
line_x as u32,
(end_y - start_y) as u32,
Color::from_rgba8(0xFF, 0xFF, 0xFF, 0.25),
);
}
}
buffer.redraw = false;
*/
let duration = instant.elapsed();
log::debug!("redraw: {:?}", duration);
}
fn on_event(
&mut self,
_state: &mut widget::Tree,
event: Event,
_layout: Layout<'_>,
_cursor_position: Point,
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
_shell: &mut Shell<'_, Message>,
) -> Status {
let mut buffer = self.buffer.write().unwrap();
match event {
Event::Keyboard(key_event) => match key_event {
KeyEvent::KeyPressed { key_code, modifiers } => {
match key_code {
KeyCode::Left => {
buffer.action(TextAction::Left);
Status::Captured
},
KeyCode::Right => {
buffer.action(TextAction::Right);
Status::Captured
},
KeyCode::Up => {
buffer.action(TextAction::Up);
Status::Captured
},
KeyCode::Down => {
buffer.action(TextAction::Down);
Status::Captured
},
KeyCode::Backspace => {
buffer.action(TextAction::Backspace);
Status::Captured
},
KeyCode::Delete => {
buffer.action(TextAction::Delete);
Status::Captured
},
KeyCode::PageUp => {
buffer.action(TextAction::PageUp);
Status::Captured
},
KeyCode::PageDown => {
buffer.action(TextAction::PageDown);
Status::Captured
},
_ => Status::Ignored,
}
},
KeyEvent::CharacterReceived(character) => {
buffer.action(TextAction::Insert(character));
Status::Captured
},
_ => Status::Ignored,
},
_ => Status::Ignored,
}
}
}
impl<'a, Message, Renderer> From<TextBox<'a>> for Element<'a, Message, Renderer>
where
Renderer: renderer::Renderer,
{
fn from(text_box: TextBox<'a>) -> Self {
Self::new(text_box)
}
}