use druid::{ piet::{Text, TextLayout, TextLayoutBuilder}, Color, Data, Event, FontFamily, Lens, LifeCycle, RenderContext, Widget, }; #[derive(Clone, Data, PartialEq, Eq)] pub enum EditMode { Normal, Insert, } pub struct AbacusEditor; #[derive(Data, Lens, Clone)] pub struct EditorData { pub raw_content: String, pub cursor_pos: usize, pub mode: EditMode, } impl Default for EditorData { fn default() -> Self { Self { raw_content: "let x = 1;\nx+1".to_string(), cursor_pos: 5, mode: EditMode::Normal, } } } impl EditorData { pub fn push_str(&mut self, s: &str) { self.raw_content.push_str(s); self.cursor_to_end(); } pub fn push(&mut self, c: char) { self.raw_content.push(c); self.cursor_to_end(); } pub fn cursor_to_end(&mut self) { self.cursor_pos = self.raw_content.len(); } pub fn delete_char_back(&mut self) { self.raw_content.pop(); self.cursor_to_end(); } } impl Widget for AbacusEditor { fn paint(&mut self, ctx: &mut druid::PaintCtx, data: &EditorData, env: &druid::Env) { let size = ctx.size(); let rect = size.to_rect(); if ctx.is_focused() { ctx.fill(rect, &Color::rgb8(10, 10, 10)); } else { ctx.fill(rect, &Color::rgb8(20, 20, 20)); } let layout = ctx .text() .new_text_layout(data.raw_content.clone()) .font(FontFamily::MONOSPACE, 24.0) .text_color(Color::rgb8(255, 255, 255)) .build() .unwrap(); dbg!(layout.rects_for_range((data.cursor_pos - 1)..data.cursor_pos)); ctx.fill( layout .rects_for_range((data.cursor_pos - 1)..data.cursor_pos) .last() .unwrap(), &Color::rgb8(50, 50, 50), ); ctx.draw_text(&layout, (1.0, 1.0)); dbg!(ctx.is_focused()); } fn event( &mut self, ctx: &mut druid::EventCtx, event: &druid::Event, data: &mut EditorData, env: &druid::Env, ) { match event { Event::KeyUp(e) => match &e.key { druid::keyboard_types::Key::Character(c) => { data.push_str(c); ctx.request_paint(); } druid::keyboard_types::Key::Enter => { data.push('\n'); ctx.request_paint(); } druid::keyboard_types::Key::Backspace => { data.delete_char_back(); ctx.request_paint(); } druid::keyboard_types::Key::Escape => { data.mode == EditMode::Normal; } e => { dbg!(e); } }, Event::MouseDown(_) => { if !ctx.is_focused() { ctx.request_focus(); } } _ => {} } } fn lifecycle( &mut self, ctx: &mut druid::LifeCycleCtx, event: &druid::LifeCycle, data: &EditorData, env: &druid::Env, ) { match event { LifeCycle::FocusChanged(_) => { ctx.request_paint(); } LifeCycle::WidgetAdded => { // ctx.register_text_input(document) } LifeCycle::BuildFocusChain => { ctx.register_for_focus(); } _ => {} } } fn update( &mut self, ctx: &mut druid::UpdateCtx, old_data: &EditorData, data: &EditorData, env: &druid::Env, ) { } fn layout( &mut self, ctx: &mut druid::LayoutCtx, bc: &druid::BoxConstraints, data: &EditorData, env: &druid::Env, ) -> druid::Size { bc.max() } }