use std::{
fmt::Debug,
hash::Hash,
ops::Deref,
sync::{
atomic::{AtomicUsize, Ordering},
Arc, LazyLock,
},
};
use altui_core::layout::Rect;
use crate::ids::WidgetId;
pub(crate) static HOVER: LazyLock<AtomicUsize> = LazyLock::new(|| AtomicUsize::new(0));
pub(crate) static ACTIVE: LazyLock<AtomicUsize> = LazyLock::new(|| AtomicUsize::new(0));
pub struct ViewCtx {
id: WidgetId,
area: Rect,
tags: Vec<Arc<str>>,
visible: bool,
interactive: bool,
button: bool,
selectable: bool,
}
impl Clone for ViewCtx {
fn clone(&self) -> Self {
Self {
id: self.id,
area: self.area,
tags: self.tags.clone(),
visible: self.visible,
interactive: self.interactive,
button: self.button,
selectable: self.button,
}
}
}
impl PartialEq for ViewCtx {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Hash for ViewCtx {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Eq for ViewCtx {}
impl Debug for ViewCtx {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NodeState")
.field("id", &self.id)
.field("area", &self.area)
.field("description", &self.tags)
.field("visible", &self.visible)
.field("interactive", &self.interactive)
.field("button", &self.button)
.field("selectable", &self.selectable)
.finish()
}
}
impl Default for ViewCtx {
fn default() -> Self {
Self::interactive()
}
}
impl Deref for ViewCtx {
type Target = Rect;
fn deref(&self) -> &Self::Target {
&self.area
}
}
impl ViewCtx {
pub fn get_id(&self) -> usize {
*self.id
}
pub fn get_widget_id(&self) -> WidgetId {
self.id
}
pub fn get_area(&self) -> Rect {
self.area
}
pub fn get_area_ref(&self) -> &Rect {
&self.area
}
pub fn interactive() -> Self {
Self::interactive_with_tags(&[""])
}
pub fn interactive_with_tags(tags: &[impl Into<Arc<str>> + Clone]) -> Self {
let tags = tags.iter().map(|tag| tag.to_owned().into()).collect();
Self {
id: WidgetId::next(),
area: Rect::default(),
tags,
visible: true,
interactive: true,
button: false,
selectable: false,
}
}
pub fn button() -> Self {
Self::button_with_tags(&[""])
}
pub fn button_with_tags(tags: &[impl Into<Arc<str>> + Clone]) -> Self {
let tags = tags.iter().map(|tag| tag.to_owned().into()).collect();
Self {
id: WidgetId::next(),
area: Rect::default(),
tags,
visible: true,
interactive: true,
button: true,
selectable: false,
}
}
pub fn selectable() -> Self {
Self::selectable_with_tags(&[""])
}
pub fn selectable_with_tags(tags: &[impl Into<Arc<str>> + Clone]) -> Self {
let tags = tags.iter().map(|tag| tag.to_owned().into()).collect();
Self {
id: WidgetId::next(),
area: Rect::default(),
tags,
visible: true,
interactive: true,
button: false,
selectable: true,
}
}
pub fn silent() -> Self {
Self::silent_with_tags(&[""])
}
pub fn silent_with_tags(tags: &[impl Into<Arc<str>> + Clone]) -> Self {
let tags = tags.iter().map(|tag| tag.to_owned().into()).collect();
Self {
id: WidgetId::next(),
area: Rect::default(),
tags,
visible: true,
interactive: false,
button: false,
selectable: false,
}
}
pub fn is_interactive(&self) -> bool {
self.interactive
}
pub fn set_interactive(&mut self, interactive: bool) {
if !interactive {
self.reset_hover();
}
self.interactive = interactive
}
pub fn is_button(&self) -> bool {
self.button
}
pub fn set_button(&mut self, button: bool) {
if button {
self.selectable = false;
}
self.button = button
}
pub fn is_selectable(&self) -> bool {
self.selectable
}
pub fn set_selectable(&mut self, selectable: bool) {
if selectable {
self.button = false;
}
self.selectable = selectable
}
pub fn is_visible(&self) -> bool {
self.visible
}
pub fn set_visible(&mut self, visible: bool) {
if !visible {
self.reset_hover();
}
self.visible = visible
}
pub fn tags(&self) -> &[Arc<str>] {
&self.tags
}
pub fn set_tag(&mut self, tag: impl Into<Arc<str>>) {
self.tags.push(tag.into());
}
pub fn set_tags(&mut self, tags: &[impl Into<Arc<str>> + Clone]) {
let tags = tags.iter().map(|tag| tag.to_owned().into()).collect();
self.tags = tags;
}
pub fn remove_tag(&mut self, tag: impl Into<Arc<str>>) {
let tag = tag.into();
self.tags.retain(|old_tag| old_tag != &tag);
}
pub fn clear_tags(&mut self) {
self.tags.clear();
}
pub fn set_area(&mut self, area: Rect) {
self.area = area;
}
pub fn set_hover(&self) {
if self.is_interactive() {
HOVER.store(self.get_id(), Ordering::Release);
}
}
#[inline(always)]
pub fn reset_hover(&self) {
if self.get_id() == HOVER.load(Ordering::Acquire) {
ACTIVE.store(0, Ordering::Release);
HOVER.store(0, Ordering::Release);
}
}
#[inline(always)]
pub fn reset_active(&self) {
if self.get_id() == ACTIVE.load(Ordering::Acquire) {
ACTIVE.store(0, Ordering::Release)
}
}
pub fn is_hover(&self) -> bool {
match self.is_interactive() {
true => self.get_id() == HOVER.load(Ordering::Acquire),
false => false,
}
}
pub fn set_active(&self) {
if self.is_interactive() && self.is_visible() {
ACTIVE.store(self.get_id(), Ordering::Release);
}
}
pub fn is_active(&self) -> bool {
match self.is_interactive() {
true => self.get_id() == ACTIVE.load(Ordering::Acquire),
false => false,
}
}
}