use iced_core::Rectangle;
use iced_core::layout::Limits;
use iced_core::window::Id;
use std::collections::BTreeSet;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::sync::{Mutex, PoisonError};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(u32)]
pub enum Anchor {
#[default]
None = 0,
Top = 1,
Bottom = 2,
Left = 3,
Right = 4,
TopLeft = 5,
BottomLeft = 6,
TopRight = 7,
BottomRight = 8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[repr(u32)]
pub enum Gravity {
#[default]
None = 0,
Top = 1,
Bottom = 2,
Left = 3,
Right = 4,
TopLeft = 5,
BottomLeft = 6,
TopRight = 7,
BottomRight = 8,
}
pub mod constraint_adjustment {
pub const NONE: u32 = 0;
pub const SLIDE_X: u32 = 1;
pub const SLIDE_Y: u32 = 2;
pub const FLIP_X: u32 = 4;
pub const FLIP_Y: u32 = 8;
pub const RESIZE_X: u32 = 16;
pub const RESIZE_Y: u32 = 32;
}
#[derive(Debug, Clone)]
pub struct PopupSettings {
pub parent: Id,
pub id: Id,
pub positioner: Positioner,
pub grab: bool,
pub blur: bool,
pub shadow: bool,
pub corner_radius: Option<u32>,
pub input_passthrough: bool,
pub tooltip_offset: Option<(i32, i32)>,
pub tooltip_anchor: Option<u32>,
pub tooltip_delay_ms: Option<u32>,
}
impl Hash for PopupSettings {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
#[derive(Debug, Clone)]
pub struct Positioner {
pub size: Option<(u32, u32)>,
pub size_limits: Limits,
pub anchor_rect: Rectangle<i32>,
pub anchor: Anchor,
pub gravity: Gravity,
pub constraint_adjustment: u32,
pub offset: (i32, i32),
pub reactive: bool,
pub window_geometry: Option<(i32, i32, i32, i32)>,
pub shadow_padding: u32,
}
impl Hash for Positioner {
fn hash<H: Hasher>(&self, state: &mut H) {
self.size.hash(state);
self.anchor_rect.x.hash(state);
self.anchor_rect.y.hash(state);
self.anchor_rect.width.hash(state);
self.anchor_rect.height.hash(state);
self.anchor.hash(state);
self.gravity.hash(state);
self.constraint_adjustment.hash(state);
self.offset.hash(state);
self.reactive.hash(state);
self.window_geometry.hash(state);
self.shadow_padding.hash(state);
}
}
impl Default for Positioner {
fn default() -> Self {
Self {
size: None,
size_limits: Limits::NONE
.min_height(1.0)
.min_width(1.0)
.max_width(400.0)
.max_height(600.0),
anchor_rect: Rectangle {
x: 0,
y: 0,
width: 1,
height: 1,
},
anchor: Anchor::None,
gravity: Gravity::None,
constraint_adjustment: constraint_adjustment::SLIDE_X
| constraint_adjustment::SLIDE_Y
| constraint_adjustment::FLIP_X
| constraint_adjustment::FLIP_Y,
offset: (0, 0),
reactive: true,
window_geometry: None,
shadow_padding: 0,
}
}
}
#[derive(Clone)]
pub enum Action {
Show {
settings: PopupSettings,
},
Hide {
id: Id,
},
Resize {
id: Id,
width: u32,
height: u32,
},
Reposition {
id: Id,
positioner: Positioner,
},
}
impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Action::Show { settings } => f
.debug_struct("Action::Show")
.field("settings", settings)
.finish(),
Action::Hide { id } => f.debug_struct("Action::Hide").field("id", id).finish(),
Action::Resize { id, width, height } => f
.debug_struct("Action::Resize")
.field("id", id)
.field("width", width)
.field("height", height)
.finish(),
Action::Reposition { id, positioner } => f
.debug_struct("Action::Reposition")
.field("id", id)
.field("positioner", positioner)
.finish(),
}
}
}
use crate::Task;
use crate::platform_specific;
use crate::task;
pub fn show<Message>(settings: PopupSettings) -> Task<Message> {
task::effect(crate::Action::PlatformSpecific(
platform_specific::Action::Wayland(super::Action::Popup(Action::Show { settings })),
))
}
pub fn hide<Message>(id: Id) -> Task<Message> {
task::effect(crate::Action::PlatformSpecific(
platform_specific::Action::Wayland(super::Action::Popup(Action::Hide { id })),
))
}
pub fn resize<Message>(id: Id, width: u32, height: u32) -> Task<Message> {
task::effect(crate::Action::PlatformSpecific(
platform_specific::Action::Wayland(super::Action::Popup(Action::Resize {
id,
width,
height,
})),
))
}
pub fn reposition<Message>(id: Id, positioner: Positioner) -> Task<Message> {
task::effect(crate::Action::PlatformSpecific(
platform_specific::Action::Wayland(super::Action::Popup(Action::Reposition {
id,
positioner,
})),
))
}
static KEYBOARD_INPUT: Mutex<BTreeSet<Id>> = Mutex::new(BTreeSet::new());
pub fn keyboard_input(id: Id, enabled: bool) {
let mut popups = KEYBOARD_INPUT
.lock()
.unwrap_or_else(PoisonError::into_inner);
let _ = if enabled {
popups.insert(id)
} else {
popups.remove(&id)
};
}
pub fn takes_keyboard_input(id: Id) -> bool {
KEYBOARD_INPUT
.lock()
.unwrap_or_else(PoisonError::into_inner)
.contains(&id)
}
pub fn forget_keyboard_input(id: Id) {
let _ = KEYBOARD_INPUT
.lock()
.unwrap_or_else(PoisonError::into_inner)
.remove(&id);
}