use std::cell::Cell;
use std::rc::Rc;
use std::time::Instant;
use objc2::rc::Retained;
use objc2::runtime::{NSObject, NSObjectProtocol};
use objc2::{define_class, msg_send, sel, DefinedClass, MainThreadOnly};
use objc2_app_kit::NSApplication;
use objc2_foundation::{MainThreadMarker, NSTimer};
const BLINK_PERIOD_SECS: f64 = 0.530;
pub(crate) struct QuadraBlinkTargetIvars {
caret_visible: Rc<Cell<bool>>,
paused_until: Rc<Cell<Instant>>,
}
define_class!(
#[unsafe(super(NSObject))]
#[thread_kind = MainThreadOnly]
#[ivars = QuadraBlinkTargetIvars]
pub(crate) struct QuadraBlinkTarget;
unsafe impl NSObjectProtocol for QuadraBlinkTarget {}
impl QuadraBlinkTarget {
#[unsafe(method(tick:))]
fn tick(&self, _timer: &NSTimer) {
let now = Instant::now();
if now < self.ivars().paused_until.get() {
return;
}
let next = !self.ivars().caret_visible.get();
self.ivars().caret_visible.set(next);
let mtm = MainThreadMarker::from(self);
let app = NSApplication::sharedApplication(mtm);
if let Some(window) = app.keyWindow() {
if let Some(view) = window.contentView() {
view.setNeedsDisplay(true);
}
}
}
}
);
impl QuadraBlinkTarget {
fn new(
mtm: MainThreadMarker,
caret_visible: Rc<Cell<bool>>,
paused_until: Rc<Cell<Instant>>,
) -> Retained<Self> {
let this = mtm.alloc::<Self>();
let this = this.set_ivars(QuadraBlinkTargetIvars {
caret_visible,
paused_until,
});
unsafe { msg_send![super(this), init] }
}
}
pub(crate) fn install_blink_timer(
mtm: MainThreadMarker,
caret_visible: Rc<Cell<bool>>,
paused_until: Rc<Cell<Instant>>,
) -> (Retained<QuadraBlinkTarget>, Retained<NSTimer>) {
let target = QuadraBlinkTarget::new(mtm, caret_visible, paused_until);
let timer: Retained<NSTimer> = unsafe {
NSTimer::scheduledTimerWithTimeInterval_target_selector_userInfo_repeats(
BLINK_PERIOD_SECS,
&target,
sel!(tick:),
None,
true,
)
};
(target, timer)
}