use cairo; use ui_model::Color; use ui::UiMutex; use shell; use shell::NvimMode; use nvim::{RepaintMode, RedrawEvents}; use std::sync::{Arc, Weak}; use glib; struct Alpha(f64); impl Alpha { pub fn show(&mut self, step: f64) -> bool { self.0 += step; if self.0 > 1.0 { self.0 = 1.0; false } else { true } } pub fn hide(&mut self, step: f64) -> bool { self.0 -= step; if self.0 < 0.0 { self.0 = 0.0; false } else { true } } } #[derive(PartialEq)] enum AnimPhase { Shown, Hide, Hidden, Show, NoFocus, Busy, } struct State { alpha: Alpha, anim_phase: AnimPhase, shell: Weak>, timer: Option, } impl State { fn new(shell: Weak>) -> State { State { alpha: Alpha(1.0), anim_phase: AnimPhase::Shown, shell: shell, timer: None, } } fn reset_to(&mut self, phase: AnimPhase) { self.alpha = Alpha(1.0); self.anim_phase = phase; if let Some(timer_id) = self.timer { glib::source_remove(timer_id); self.timer = None; } } } pub struct Cursor { state: Arc>, } impl Cursor { pub fn new(shell: Weak>) -> Cursor { Cursor { state: Arc::new(UiMutex::new(State::new(shell))) } } pub fn start(&mut self) { let state = self.state.clone(); let mut mut_state = self.state.borrow_mut(); mut_state.reset_to(AnimPhase::Shown); mut_state.timer = Some(glib::timeout_add(500, move || anim_step(&state))); } pub fn reset_state(&mut self) { self.start(); } pub fn enter_focus(&mut self) { self.start(); } pub fn leave_focus(&mut self) { self.state.borrow_mut().reset_to(AnimPhase::NoFocus); } pub fn busy_on(&mut self) { self.state.borrow_mut().reset_to(AnimPhase::Busy); } pub fn busy_off(&mut self) { self.start(); } pub fn draw(&self, ctx: &cairo::Context, shell: &shell::State, char_width: f64, line_height: f64, line_y: f64, double_width: bool, bg: &Color) { let state = self.state.borrow(); if state.anim_phase == AnimPhase::Busy { return; } let current_point = ctx.get_current_point(); ctx.set_source_rgba(1.0 - bg.0, 1.0 - bg.1, 1.0 - bg.2, 0.6 * state.alpha.0); let cursor_width = if shell.mode == NvimMode::Insert { char_width / 5.0 } else { if double_width { char_width * 2.0 } else { char_width } }; ctx.rectangle(current_point.0, line_y, cursor_width, line_height); if state.anim_phase == AnimPhase::NoFocus { ctx.stroke(); } else { ctx.fill(); } } } fn anim_step(state: &Arc>) -> glib::Continue { let mut mut_state = state.borrow_mut(); let next_event = match mut_state.anim_phase { AnimPhase::Shown => { mut_state.anim_phase = AnimPhase::Hide; Some(60) } AnimPhase::Hide => { if !mut_state.alpha.hide(0.3) { mut_state.anim_phase = AnimPhase::Hidden; Some(300) } else { None } } AnimPhase::Hidden => { mut_state.anim_phase = AnimPhase::Show; Some(60) } AnimPhase::Show => { if !mut_state.alpha.show(0.3) { mut_state.anim_phase = AnimPhase::Shown; Some(500) } else { None } } AnimPhase::NoFocus => None, AnimPhase::Busy => None, }; let shell = mut_state.shell.upgrade().unwrap(); let shell = shell.borrow(); let point = shell.model.cur_point(); shell.on_redraw(&RepaintMode::Area(point)); if let Some(timeout) = next_event { let moved_state = state.clone(); mut_state.timer = Some(glib::timeout_add(timeout, move || anim_step(&moved_state))); glib::Continue(false) } else { glib::Continue(true) } } impl Drop for Cursor { fn drop(&mut self) { if let Some(timer_id) = self.state.borrow().timer { glib::source_remove(timer_id); } } }