use crate::{FocusBuilder, HasFocus, Navigation, ratatui};
use std::cell::{Cell, RefCell};
use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::ptr;
use std::rc::Rc;
#[derive(Clone, Default)]
pub struct FocusFlag(Rc<FocusFlagCore>);
impl PartialEq for FocusFlag {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for FocusFlag {}
impl Hash for FocusFlag {
fn hash<H: Hasher>(&self, state: &mut H) {
ptr::hash(Rc::as_ptr(&self.0), state);
}
}
impl Display for FocusFlag {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let name = self.0.name.borrow();
if let Some(name) = &*name {
write!(f, "|{}|", name)
} else {
write!(f, "")
}
}
}
impl HasFocus for FocusFlag {
fn build(&self, builder: &mut FocusBuilder) {
builder.leaf_widget(self);
}
fn focus(&self) -> FocusFlag {
self.clone()
}
fn area(&self) -> ratatui::layout::Rect {
ratatui::layout::Rect::default()
}
fn area_z(&self) -> u16 {
0
}
fn navigable(&self) -> Navigation {
Navigation::Regular
}
}
struct FocusFlagCore {
name: RefCell<Option<Box<str>>>,
focus: Cell<bool>,
gained: Cell<bool>,
on_gained: RefCell<Option<Box<dyn Fn()>>>,
lost: Cell<bool>,
on_lost: RefCell<Option<Box<dyn Fn()>>>,
mouse_focus: Cell<bool>,
}
impl Debug for FocusFlag {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FocusFlag")
.field("name", &self.0.name)
.field("focus", &self.0.focus.get())
.field("widget_id", &self.widget_id())
.field("gained", &self.0.gained.get())
.field("on_gained", &self.0.on_gained.borrow().is_some())
.field("lost", &self.0.lost.get())
.field("on_lost", &self.0.on_lost.borrow().is_some())
.finish()
}
}
impl FocusFlag {
pub fn new() -> Self {
Self::default()
}
pub fn new_instance(&self) -> Self {
Self(Rc::new(self.0.fake_clone()))
}
pub fn widget_id(&self) -> usize {
Rc::as_ptr(&self.0) as usize
}
#[deprecated(
since = "1.4.0",
note = "to dangerous, use FocusFlag::new().with_name(..) or FocusFlag::fake_clone(..) for a clone."
)]
pub fn named(name: impl AsRef<str>) -> Self {
Self(Rc::new(FocusFlagCore::default().named(name.as_ref())))
}
pub fn with_name(self, name: &str) -> Self {
self.set_name(name);
self
}
#[inline]
pub fn get(&self) -> bool {
self.0.focus.get()
}
#[inline]
pub fn set(&self, focus: bool) {
self.0.focus.set(focus);
}
#[inline]
pub fn name(&self) -> Box<str> {
self.0.name.borrow().clone().unwrap_or_default()
}
#[inline]
pub fn set_name(&self, name: &str) {
*self.0.name.borrow_mut() = Some(Box::from(name))
}
#[inline]
pub fn lost(&self) -> bool {
self.0.lost.get()
}
#[inline]
pub fn set_lost(&self, lost: bool) {
self.0.lost.set(lost);
}
#[inline]
pub fn on_lost(&self, on_lost: impl Fn() + 'static) {
*(self.0.on_lost.borrow_mut()) = Some(Box::new(on_lost));
}
#[inline]
pub fn call_on_lost(&self) -> bool {
let borrow = self.0.on_lost.borrow();
if let Some(f) = borrow.as_ref() {
f();
true
} else {
false
}
}
#[inline]
pub fn gained(&self) -> bool {
self.0.gained.get()
}
#[inline]
pub fn set_gained(&self, gained: bool) {
self.0.gained.set(gained);
}
#[inline]
pub fn on_gained(&self, on_gained: impl Fn() + 'static) {
*(self.0.on_gained.borrow_mut()) = Some(Box::new(on_gained));
}
#[inline]
pub fn call_on_gained(&self) -> bool {
let borrow = self.0.on_gained.borrow();
if let Some(f) = borrow.as_ref() {
f();
true
} else {
false
}
}
#[inline]
pub fn set_mouse_focus(&self, mf: bool) {
self.0.mouse_focus.set(mf);
}
#[inline]
pub fn mouse_focus(&self) -> bool {
self.0.mouse_focus.get()
}
#[inline]
pub fn clear(&self) {
self.0.focus.set(false);
self.0.lost.set(false);
self.0.gained.set(false);
self.0.mouse_focus.set(true);
}
}
impl Default for FocusFlagCore {
fn default() -> Self {
Self {
name: RefCell::new(None),
focus: Cell::new(false),
gained: Cell::new(false),
on_gained: RefCell::new(None),
lost: Cell::new(false),
on_lost: RefCell::new(None),
mouse_focus: Cell::new(true),
}
}
}
impl FocusFlagCore {
#[inline(always)]
pub(crate) fn named(self, name: &str) -> Self {
*self.name.borrow_mut() = Some(Box::from(name));
self
}
pub(crate) fn fake_clone(&self) -> Self {
Self {
name: self.name.clone(),
focus: Cell::new(self.focus.get()),
gained: Cell::new(self.gained.get()),
on_gained: RefCell::new(None),
lost: Cell::new(self.lost.get()),
on_lost: RefCell::new(None),
mouse_focus: Cell::new(self.mouse_focus.get()),
}
}
}