use std::any::{self, Any};
use std::borrow::Borrow;
use std::collections::{hash_map::Entry, HashMap};
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
use crate::kurbo::RoundedRectRadii;
use crate::localization::L10nManager;
use crate::text::FontDescriptor;
use crate::{ArcStr, Color, Data, Insets, Point, Rect, Size};
#[derive(Clone)]
pub struct Env(Arc<EnvImpl>);
#[derive(Debug, Clone)]
struct EnvImpl {
map: HashMap<ArcStr, Value>,
l10n: Option<Arc<L10nManager>>,
}
#[derive(Clone, Debug, PartialEq, Eq, Data)]
pub struct Key<T> {
key: &'static str,
value_type: PhantomData<T>,
}
#[derive(Clone, Data)]
#[allow(missing_docs)]
pub enum Value {
Point(Point),
Size(Size),
Rect(Rect),
Insets(Insets),
Color(Color),
Float(f64),
Bool(bool),
UnsignedInt(u64),
String(ArcStr),
Font(FontDescriptor),
RoundedRectRadii(RoundedRectRadii),
Other(Arc<dyn Any + Send + Sync>),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum KeyOrValue<T> {
Concrete(T),
Key(Key<T>),
}
impl<T: Data> Data for KeyOrValue<T> {
fn same(&self, other: &Self) -> bool {
match (self, other) {
(Self::Concrete(a), Self::Concrete(b)) => a.same(b),
(Self::Key(a), Self::Key(b)) => a.same(b),
_ => false,
}
}
}
pub trait KeyLike<T> {
fn changed(&self, old: &Env, new: &Env) -> bool;
}
impl<T: ValueType> KeyLike<T> for Key<T> {
fn changed(&self, old: &Env, new: &Env) -> bool {
!old.get_untyped(self).same(new.get_untyped(self))
}
}
impl<T> KeyLike<T> for KeyOrValue<T> {
fn changed(&self, old: &Env, new: &Env) -> bool {
match self {
KeyOrValue::Concrete(_) => false,
KeyOrValue::Key(key) => !old.get_untyped(key).same(new.get_untyped(key)),
}
}
}
pub trait ValueType: Sized + Clone + Into<Value> {
fn try_from_value(v: &Value) -> Result<Self, ValueTypeError>;
}
#[derive(Debug, Clone)]
pub struct ValueTypeError {
expected: &'static str,
found: Value,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct MissingKeyError {
key: Arc<str>,
}
impl Env {
pub(crate) const DEBUG_PAINT: Key<bool> = Key::new("org.linebender.druid.built-in.debug-paint");
pub(crate) const DEBUG_WIDGET_ID: Key<bool> =
Key::new("org.linebender.druid.built-in.debug-widget-id");
pub const DEBUG_WIDGET: Key<bool> = Key::new("org.linebender.druid.built-in.debug-widget");
pub fn get<V: ValueType>(&self, key: impl Borrow<Key<V>>) -> V {
match self.try_get(key) {
Ok(value) => value,
Err(err) => panic!("{}", err),
}
}
pub fn try_get<V: ValueType>(&self, key: impl Borrow<Key<V>>) -> Result<V, MissingKeyError> {
self.0
.map
.get(key.borrow().key)
.map(|value| value.to_inner_unchecked())
.ok_or(MissingKeyError {
key: key.borrow().key.into(),
})
}
pub fn get_untyped<V>(&self, key: impl Borrow<Key<V>>) -> &Value {
match self.try_get_untyped(key) {
Ok(val) => val,
Err(err) => panic!("{}", err),
}
}
pub fn try_get_untyped<V>(&self, key: impl Borrow<Key<V>>) -> Result<&Value, MissingKeyError> {
self.0.map.get(key.borrow().key).ok_or(MissingKeyError {
key: key.borrow().key.into(),
})
}
pub fn get_all(&self) -> impl ExactSizeIterator<Item = (&ArcStr, &Value)> {
self.0.map.iter()
}
pub fn adding<V: ValueType>(mut self, key: Key<V>, value: impl Into<V>) -> Env {
let env = Arc::make_mut(&mut self.0);
env.map.insert(key.into(), value.into().into());
self
}
pub fn set<V: ValueType>(&mut self, key: Key<V>, value: impl Into<V>) {
let value = value.into().into();
self.try_set_raw(key, value).unwrap();
}
pub fn try_set_raw<V: ValueType>(
&mut self,
key: Key<V>,
raw: Value,
) -> Result<(), ValueTypeError> {
let env = Arc::make_mut(&mut self.0);
let key = key.into();
match env.map.entry(key) {
Entry::Occupied(mut e) => {
let existing = e.get_mut();
if !existing.is_same_type(&raw) {
return Err(ValueTypeError::new(any::type_name::<V>(), raw));
}
*existing = raw;
}
Entry::Vacant(e) => {
e.insert(raw);
}
}
Ok(())
}
pub(crate) fn localization_manager(&self) -> Option<&L10nManager> {
self.0.l10n.as_deref()
}
#[doc(hidden)]
pub fn get_debug_color(&self, id: u64) -> Color {
let color_num = id as usize % DEBUG_COLOR.len();
DEBUG_COLOR[color_num]
}
}
impl std::fmt::Debug for Env {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("Env")
.field("l10n", &self.0.l10n)
.field("map", &self.0.map)
.finish()
}
}
impl<T> Key<T> {
pub const fn new(key: &'static str) -> Self {
Key {
key,
value_type: PhantomData,
}
}
}
impl Key<()> {
pub const fn untyped(key: &'static str) -> Self {
Key {
key,
value_type: PhantomData,
}
}
pub const fn raw(&self) -> &'static str {
self.key
}
}
impl Value {
pub fn to_inner_unchecked<V: ValueType>(&self) -> V {
match ValueType::try_from_value(self) {
Ok(v) => v,
Err(s) => panic!("{}", s),
}
}
fn is_same_type(&self, other: &Value) -> bool {
use Value::*;
matches!(
(self, other),
(Point(_), Point(_))
| (Size(_), Size(_))
| (Rect(_), Rect(_))
| (Insets(_), Insets(_))
| (Color(_), Color(_))
| (Float(_), Float(_))
| (Bool(_), Bool(_))
| (UnsignedInt(_), UnsignedInt(_))
| (String(_), String(_))
| (Font(_), Font(_))
| (RoundedRectRadii(_), RoundedRectRadii(_))
)
}
}
impl Debug for Value {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
match self {
Value::Point(p) => write!(f, "Point {p:?}"),
Value::Size(s) => write!(f, "Size {s:?}"),
Value::Rect(r) => write!(f, "Rect {r:?}"),
Value::Insets(i) => write!(f, "Insets {i:?}"),
Value::Color(c) => write!(f, "Color {c:?}"),
Value::Float(x) => write!(f, "Float {x}"),
Value::Bool(b) => write!(f, "Bool {b}"),
Value::UnsignedInt(x) => write!(f, "UnsignedInt {x}"),
Value::String(s) => write!(f, "String {s:?}"),
Value::Font(font) => write!(f, "Font {font:?}"),
Value::RoundedRectRadii(radius) => write!(f, "RoundedRectRadii {radius:?}"),
Value::Other(other) => write!(f, "{other:?}"),
}
}
}
impl Data for Env {
fn same(&self, other: &Env) -> bool {
Arc::ptr_eq(&self.0, &other.0) || self.0.deref().same(other.0.deref())
}
}
impl Data for EnvImpl {
fn same(&self, other: &EnvImpl) -> bool {
self.map.len() == other.map.len()
&& self
.map
.iter()
.all(|(k, v1)| other.map.get(k).map(|v2| v1.same(v2)).unwrap_or(false))
}
}
static DEBUG_COLOR: &[Color] = &[
Color::rgb8(230, 25, 75),
Color::rgb8(60, 180, 75),
Color::rgb8(255, 225, 25),
Color::rgb8(0, 130, 200),
Color::rgb8(245, 130, 48),
Color::rgb8(70, 240, 240),
Color::rgb8(240, 50, 230),
Color::rgb8(250, 190, 190),
Color::rgb8(0, 128, 128),
Color::rgb8(230, 190, 255),
Color::rgb8(170, 110, 40),
Color::rgb8(255, 250, 200),
Color::rgb8(128, 0, 0),
Color::rgb8(170, 255, 195),
Color::rgb8(0, 0, 128),
Color::rgb8(128, 128, 128),
Color::rgb8(255, 255, 255),
Color::rgb8(0, 0, 0),
];
impl Env {
pub fn empty() -> Self {
Env(Arc::new(EnvImpl {
l10n: None,
map: HashMap::new(),
}))
}
pub(crate) fn with_default_i10n() -> Self {
Env::with_i10n(vec!["builtin.ftl".into()], "./resources/i18n/")
}
pub(crate) fn with_i10n(resources: Vec<String>, base_dir: &str) -> Self {
let l10n = L10nManager::new(resources, base_dir);
let inner = EnvImpl {
l10n: Some(Arc::new(l10n)),
map: HashMap::new(),
};
let env = Env(Arc::new(inner))
.adding(Env::DEBUG_PAINT, false)
.adding(Env::DEBUG_WIDGET_ID, false)
.adding(Env::DEBUG_WIDGET, false);
crate::theme::add_to_env(env)
}
}
impl<T> From<Key<T>> for ArcStr {
fn from(src: Key<T>) -> ArcStr {
ArcStr::from(src.key)
}
}
impl ValueTypeError {
fn new(expected: &'static str, found: Value) -> ValueTypeError {
ValueTypeError { expected, found }
}
}
impl std::fmt::Display for ValueTypeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"Incorrect value type: expected {} found {:?}",
self.expected, self.found
)
}
}
impl MissingKeyError {
pub fn raw_key(&self) -> &str {
&self.key
}
}
impl std::fmt::Display for MissingKeyError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Missing key: '{}'", self.key)
}
}
impl std::error::Error for ValueTypeError {}
impl std::error::Error for MissingKeyError {}
macro_rules! impl_value_type {
($ty:ty, $var:ident) => {
impl ValueType for $ty {
fn try_from_value(value: &Value) -> Result<Self, ValueTypeError> {
match value {
Value::$var(f) => Ok(f.to_owned()),
other => Err(ValueTypeError::new(any::type_name::<$ty>(), other.clone())),
}
}
}
impl From<$ty> for Value {
fn from(val: $ty) -> Value {
Value::$var(val)
}
}
};
}
impl_value_type!(f64, Float);
impl_value_type!(bool, Bool);
impl_value_type!(u64, UnsignedInt);
impl_value_type!(Color, Color);
impl_value_type!(Rect, Rect);
impl_value_type!(Point, Point);
impl_value_type!(Size, Size);
impl_value_type!(Insets, Insets);
impl_value_type!(ArcStr, String);
impl_value_type!(FontDescriptor, Font);
impl_value_type!(RoundedRectRadii, RoundedRectRadii);
impl<T: 'static + Send + Sync> From<Arc<T>> for Value {
fn from(this: Arc<T>) -> Value {
Value::Other(this)
}
}
impl<T: 'static + Send + Sync> ValueType for Arc<T> {
fn try_from_value(v: &Value) -> Result<Self, ValueTypeError> {
let err = ValueTypeError {
expected: any::type_name::<T>(),
found: v.clone(),
};
match v {
Value::Other(o) => o.clone().downcast::<T>().map_err(|_| err),
_ => Err(err),
}
}
}
impl<T: ValueType> KeyOrValue<T> {
pub fn resolve(&self, env: &Env) -> T {
match self {
KeyOrValue::Concrete(ref value) => value.to_owned(),
KeyOrValue::Key(key) => env.get(key),
}
}
}
impl<T: Into<Value>> From<T> for KeyOrValue<T> {
fn from(value: T) -> KeyOrValue<T> {
KeyOrValue::Concrete(value)
}
}
impl<T: ValueType> From<Key<T>> for KeyOrValue<T> {
fn from(key: Key<T>) -> KeyOrValue<T> {
KeyOrValue::Key(key)
}
}
macro_rules! key_or_value_from_concrete {
($from:ty => $to:ty) => {
impl From<$from> for KeyOrValue<$to> {
fn from(f: $from) -> KeyOrValue<$to> {
KeyOrValue::Concrete(f.into())
}
}
};
}
key_or_value_from_concrete!(f64 => Insets);
key_or_value_from_concrete!((f64, f64) => Insets);
key_or_value_from_concrete!((f64, f64, f64, f64) => Insets);
key_or_value_from_concrete!(f64 => RoundedRectRadii);
key_or_value_from_concrete!((f64, f64, f64, f64) => RoundedRectRadii);
#[cfg(test)]
mod tests {
use super::*;
use test_log::test;
#[test]
fn string_key_or_value() {
const MY_KEY: Key<ArcStr> = Key::new("org.linebender.test.my-string-key");
let env = Env::empty().adding(MY_KEY, "Owned");
assert_eq!(env.get(MY_KEY).as_ref(), "Owned");
let key: KeyOrValue<ArcStr> = MY_KEY.into();
let value: KeyOrValue<ArcStr> = ArcStr::from("Owned").into();
assert_eq!(key.resolve(&env), value.resolve(&env));
}
#[test]
fn key_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Key<()>>();
}
}