Skip to content

Commit e47f0cc

Browse files
committed
Remove fancy lifetime stuff from Env
Previously, we were doing a bunch of gymnastics in order to allow theEnv to return either borrowed or owned data, depending on the type; the borrowed case was only used for Strings. This code was brittle and hard to understand, and ultimately I think it was solving the wrong problem: instead of trying to avoid cloning expensive data when we get things out of the map, I would prefer to just not have expensive-to-clone things in there in the first place. This introduces the ArcStr type alias for Arc<str>, which is my preferred type for simple strings in druid. Realistically we should have a conversation soon about string types, since we're almost certainly going to be bringing in the xi Rope structure to represent editable text, and we may just want to use that for everything; in that case it will be easy enough to just replace ArcStr with Rope next week. This opens up a bunch of possible future work. The problem I'm most focused on right now is how to synthesize values out of the env; this is sort of like 'env lensing', and it wasn't possible because of the lifetimes. I also think that we should consider removing the `Data` impl from String, but I want to wait until we have a clearer sense of what our text types end up being.
1 parent a1a37a6 commit e47f0cc

File tree

8 files changed

+56
-83
lines changed

8 files changed

+56
-83
lines changed

druid/examples/styled_text.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
1717
use druid::widget::{Checkbox, Flex, Label, MainAxisAlignment, Painter, Parse, Stepper, TextBox};
1818
use druid::{
19-
theme, AppLauncher, Color, Data, Key, Lens, LensExt, LensWrap, LocalizedString, PlatformError,
20-
RenderContext, Widget, WidgetExt, WindowDesc,
19+
theme, AppLauncher, ArcStr, Color, Data, Key, Lens, LensExt, LensWrap, LocalizedString,
20+
PlatformError, RenderContext, Widget, WidgetExt, WindowDesc,
2121
};
2222
use std::fmt::Display;
2323

2424
// This is a custom key we'll use with Env to set and get our text size.
2525
const MY_CUSTOM_TEXT_SIZE: Key<f64> = Key::new("styled_text.custom_text_size");
26-
const MY_CUSTOM_FONT: Key<&str> = Key::new("styled_text.custom_font");
26+
const MY_CUSTOM_FONT: Key<ArcStr> = Key::new("styled_text.custom_font");
2727

2828
#[derive(Clone, Lens, Data)]
2929
struct AppData {
@@ -99,7 +99,7 @@ fn ui_builder() -> impl Widget<AppData> {
9999
if data.mono {
100100
env.set(MY_CUSTOM_FONT, "monospace");
101101
} else {
102-
env.set(MY_CUSTOM_FONT, env.get(theme::FONT_NAME).to_string());
102+
env.set(MY_CUSTOM_FONT, env.get(theme::FONT_NAME));
103103
}
104104
});
105105

druid/src/data.rs

+7
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ pub trait Data: Clone + 'static {
119119
//// ANCHOR_END: same_fn
120120
}
121121

122+
/// A reference counted string slice.
123+
///
124+
/// This is a data-friendly way to represent strings in druid. Unlike `String`
125+
/// it cannot be mutated, but unlike `String` it can be cheaply cloned.
126+
pub type ArcStr = Arc<str>;
127+
122128
/// An impl of `Data` suitable for simple types.
123129
///
124130
/// The `same` method is implemented with equality, so the type should
@@ -145,6 +151,7 @@ impl_data_simple!(u64);
145151
impl_data_simple!(usize);
146152
impl_data_simple!(char);
147153
impl_data_simple!(bool);
154+
//TODO: remove me!?
148155
impl_data_simple!(String);
149156

150157
impl Data for f32 {

druid/src/env.rs

+35-69
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::ops::Deref;
2323
use std::sync::Arc;
2424

2525
use crate::localization::L10nManager;
26-
use crate::{Color, Data, Point, Rect, Size};
26+
use crate::{ArcStr, Color, Data, Point, Rect, Size};
2727

2828
/// An environment passed down through all widget traversals.
2929
///
@@ -52,7 +52,7 @@ pub struct Env(Arc<EnvImpl>);
5252

5353
#[derive(Clone)]
5454
struct EnvImpl {
55-
map: HashMap<String, Value>,
55+
map: HashMap<ArcStr, Value>,
5656
debug_colors: Vec<Color>,
5757
l10n: Arc<L10nManager>,
5858
}
@@ -107,7 +107,7 @@ pub enum Value {
107107
Float(f64),
108108
Bool(bool),
109109
UnsignedInt(u64),
110-
String(String),
110+
String(ArcStr),
111111
}
112112
// ANCHOR_END: value_type
113113

@@ -132,17 +132,9 @@ pub enum KeyOrValue<T> {
132132
}
133133

134134
/// Values which can be stored in an environment.
135-
///
136-
/// Note that for "expensive" types this is the reference. For example,
137-
/// for strings, this trait is implemented on `&'a str`. The trait is
138-
/// parametrized on a lifetime so that it can be used for references in
139-
/// this way.
140-
pub trait ValueType<'a>: Sized {
141-
/// The corresponding owned type.
142-
type Owned: Into<Value>;
143-
135+
pub trait ValueType: Sized + Into<Value> {
144136
/// Attempt to convert the generic `Value` into this type.
145-
fn try_from_value(v: &'a Value) -> Result<Self, ValueTypeError>;
137+
fn try_from_value(v: &Value) -> Result<Self, ValueTypeError>;
146138
}
147139

148140
/// The error type for environment access.
@@ -214,7 +206,7 @@ impl Env {
214206
/// # Panics
215207
///
216208
/// Panics if the key is not found, or if it is present with the wrong type.
217-
pub fn get<'a, V: ValueType<'a>>(&'a self, key: impl Borrow<Key<V>>) -> V {
209+
pub fn get<V: ValueType>(&self, key: impl Borrow<Key<V>>) -> V {
218210
match self.try_get(key) {
219211
Ok(value) => value,
220212
Err(err) => panic!("{}", err),
@@ -228,10 +220,7 @@ impl Env {
228220
/// # Panics
229221
///
230222
/// Panics if the value for the key is found, but has the wrong type.
231-
pub fn try_get<'a, V: ValueType<'a>>(
232-
&'a self,
233-
key: impl Borrow<Key<V>>,
234-
) -> Result<V, MissingKeyError> {
223+
pub fn try_get<V: ValueType>(&self, key: impl Borrow<Key<V>>) -> Result<V, MissingKeyError> {
235224
self.0
236225
.map
237226
.get(key.borrow().key)
@@ -275,12 +264,12 @@ impl Env {
275264
///
276265
/// *WARNING:* This is not intended for general use, but only for inspecting an `Env` e.g.
277266
/// for debugging, theme editing, and theme loading.
278-
pub fn get_all(&self) -> impl ExactSizeIterator<Item = (&String, &Value)> {
267+
pub fn get_all(&self) -> impl ExactSizeIterator<Item = (&ArcStr, &Value)> {
279268
self.0.map.iter()
280269
}
281270

282271
/// Adds a key/value, acting like a builder.
283-
pub fn adding<'a, V: ValueType<'a>>(mut self, key: Key<V>, value: impl Into<V::Owned>) -> Env {
272+
pub fn adding<V: ValueType>(mut self, key: Key<V>, value: impl Into<V>) -> Env {
284273
let env = Arc::make_mut(&mut self.0);
285274
env.map.insert(key.into(), value.into().into());
286275
self
@@ -292,7 +281,7 @@ impl Env {
292281
///
293282
/// Panics if the environment already has a value for the key, but it is
294283
/// of a different type.
295-
pub fn set<'a, V: ValueType<'a>>(&'a mut self, key: Key<V>, value: impl Into<V::Owned>) {
284+
pub fn set<V: ValueType>(&mut self, key: Key<V>, value: impl Into<V>) {
296285
let env = Arc::make_mut(&mut self.0);
297286
let value = value.into().into();
298287
let key = key.into();
@@ -367,7 +356,7 @@ impl Value {
367356
/// # Panics
368357
///
369358
/// Panics when the value variant doesn't match the provided type.
370-
pub fn to_inner_unchecked<'a, V: ValueType<'a>>(&'a self) -> V {
359+
pub fn to_inner_unchecked<V: ValueType>(&self) -> V {
371360
match ValueType::try_from_value(self) {
372361
Ok(v) => v,
373362
Err(s) => panic!("{}", s),
@@ -418,7 +407,7 @@ impl Data for Value {
418407
(Float(f1), Float(f2)) => f1.same(&f2),
419408
(Bool(b1), Bool(b2)) => b1 == b2,
420409
(UnsignedInt(f1), UnsignedInt(f2)) => f1.same(&f2),
421-
(String(s1), String(s2)) => s1 == s2,
410+
(String(s1), String(s2)) => s1.same(s2),
422411
_ => false,
423412
}
424413
}
@@ -480,9 +469,9 @@ impl Default for Env {
480469
}
481470
}
482471

483-
impl<T> From<Key<T>> for String {
484-
fn from(src: Key<T>) -> String {
485-
String::from(src.key)
472+
impl<T> From<Key<T>> for ArcStr {
473+
fn from(src: Key<T>) -> ArcStr {
474+
ArcStr::from(src.key)
486475
}
487476
}
488477

@@ -518,10 +507,9 @@ impl std::error::Error for ValueTypeError {}
518507
impl std::error::Error for MissingKeyError {}
519508

520509
/// Use this macro for types which are cheap to clone (ie all `Copy` types).
521-
macro_rules! impl_value_type_owned {
510+
macro_rules! impl_value_type {
522511
($ty:ty, $var:ident) => {
523-
impl<'a> ValueType<'a> for $ty {
524-
type Owned = $ty;
512+
impl ValueType for $ty {
525513
fn try_from_value(value: &Value) -> Result<Self, ValueTypeError> {
526514
match value {
527515
Value::$var(f) => Ok(f.to_owned()),
@@ -538,57 +526,35 @@ macro_rules! impl_value_type_owned {
538526
};
539527
}
540528

541-
/// Use this macro for types which require allocation but are not too
542-
/// expensive to clone.
543-
macro_rules! impl_value_type_borrowed {
544-
($ty:ty, $owned:ty, $var:ident) => {
545-
impl<'a> ValueType<'a> for &'a $ty {
546-
type Owned = $owned;
547-
fn try_from_value(value: &'a Value) -> Result<Self, ValueTypeError> {
548-
match value {
549-
Value::$var(f) => Ok(f),
550-
other => Err(ValueTypeError::new(any::type_name::<$ty>(), other.clone())),
551-
}
552-
}
553-
}
554-
555-
impl Into<Value> for $owned {
556-
fn into(self) -> Value {
557-
Value::$var(self)
558-
}
559-
}
560-
};
561-
}
562-
563-
impl_value_type_owned!(f64, Float);
564-
impl_value_type_owned!(bool, Bool);
565-
impl_value_type_owned!(u64, UnsignedInt);
566-
impl_value_type_owned!(Color, Color);
567-
impl_value_type_owned!(Rect, Rect);
568-
impl_value_type_owned!(Point, Point);
569-
impl_value_type_owned!(Size, Size);
570-
impl_value_type_borrowed!(str, String, String);
529+
impl_value_type!(f64, Float);
530+
impl_value_type!(bool, Bool);
531+
impl_value_type!(u64, UnsignedInt);
532+
impl_value_type!(Color, Color);
533+
impl_value_type!(Rect, Rect);
534+
impl_value_type!(Point, Point);
535+
impl_value_type!(Size, Size);
536+
impl_value_type!(ArcStr, String);
571537

572-
impl<'a, T: ValueType<'a>> KeyOrValue<T> {
538+
impl<T: ValueType> KeyOrValue<T> {
573539
/// Resolve the concrete type `T` from this `KeyOrValue`, using the provided
574540
/// [`Env`] if required.
575541
///
576542
/// [`Env`]: struct.Env.html
577-
pub fn resolve(&'a self, env: &'a Env) -> T {
543+
pub fn resolve(&self, env: &Env) -> T {
578544
match self {
579545
KeyOrValue::Concrete(value) => value.to_inner_unchecked(),
580546
KeyOrValue::Key(key) => env.get(key),
581547
}
582548
}
583549
}
584550

585-
impl<'a, V: Into<Value>, T: ValueType<'a, Owned = V>> From<V> for KeyOrValue<T> {
586-
fn from(value: V) -> KeyOrValue<T> {
551+
impl<T: Into<Value>> From<T> for KeyOrValue<T> {
552+
fn from(value: T) -> KeyOrValue<T> {
587553
KeyOrValue::Concrete(value.into())
588554
}
589555
}
590556

591-
impl<'a, T: ValueType<'a>> From<Key<T>> for KeyOrValue<T> {
557+
impl<T: ValueType> From<Key<T>> for KeyOrValue<T> {
592558
fn from(key: Key<T>) -> KeyOrValue<T> {
593559
KeyOrValue::Key(key)
594560
}
@@ -600,12 +566,12 @@ mod tests {
600566

601567
#[test]
602568
fn string_key_or_value() {
603-
const MY_KEY: Key<&str> = Key::new("test.my-string-key");
604-
let env = Env::default().adding(MY_KEY, "Owned".to_string());
605-
assert_eq!(env.get(MY_KEY), "Owned");
569+
const MY_KEY: Key<ArcStr> = Key::new("test.my-string-key");
570+
let env = Env::default().adding(MY_KEY, "Owned");
571+
assert_eq!(env.get(MY_KEY).as_ref(), "Owned");
606572

607-
let key: KeyOrValue<&str> = MY_KEY.into();
608-
let value: KeyOrValue<&str> = "Owned".to_string().into();
573+
let key: KeyOrValue<ArcStr> = MY_KEY.into();
574+
let value: KeyOrValue<ArcStr> = ArcStr::from("Owned").into();
609575

610576
assert_eq!(key.resolve(&env), value.resolve(&env));
611577
}

druid/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ pub use app_delegate::{AppDelegate, DelegateCtx};
185185
pub use box_constraints::BoxConstraints;
186186
pub use command::{sys as commands, Command, Selector, SingleUse, Target};
187187
pub use contexts::{EventCtx, LayoutCtx, LifeCycleCtx, PaintCtx, UpdateCtx};
188-
pub use data::Data;
188+
pub use data::{ArcStr, Data};
189189
pub use env::{Env, Key, KeyOrValue, Value, ValueType};
190190
pub use event::{Event, InternalEvent, InternalLifeCycle, LifeCycle};
191191
pub use ext_event::{ExtEventError, ExtEventSink};

druid/src/theme.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#![allow(missing_docs)]
1818
use crate::piet::Color;
1919

20-
use crate::{Env, Key};
20+
use crate::{ArcStr, Env, Key};
2121

2222
pub const WINDOW_BACKGROUND_COLOR: Key<Color> = Key::new("window_background_color");
2323

@@ -41,7 +41,7 @@ pub const SELECTION_COLOR: Key<Color> = Key::new("selection_color");
4141
pub const SELECTION_TEXT_COLOR: Key<Color> = Key::new("selection_text_color");
4242
pub const CURSOR_COLOR: Key<Color> = Key::new("cursor_color");
4343

44-
pub const FONT_NAME: Key<&str> = Key::new("font_name");
44+
pub const FONT_NAME: Key<ArcStr> = Key::new("font_name");
4545
pub const TEXT_SIZE_NORMAL: Key<f64> = Key::new("text_size_normal");
4646
pub const TEXT_SIZE_LARGE: Key<f64> = Key::new("text_size_large");
4747
pub const BASIC_WIDGET_HEIGHT: Key<f64> = Key::new("basic_widget_height");

druid/src/widget/label.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::piet::{
1919
TextLayoutBuilder, UnitPoint,
2020
};
2121
use crate::{
22-
theme, BoxConstraints, Data, Env, Event, EventCtx, KeyOrValue, LayoutCtx, LifeCycle,
22+
theme, ArcStr, BoxConstraints, Data, Env, Event, EventCtx, KeyOrValue, LayoutCtx, LifeCycle,
2323
LifeCycleCtx, LocalizedString, PaintCtx, Point, Size, UpdateCtx, Widget,
2424
};
2525

@@ -55,7 +55,7 @@ pub struct Label<T> {
5555
text: LabelText<T>,
5656
color: KeyOrValue<Color>,
5757
size: KeyOrValue<f64>,
58-
font: KeyOrValue<&'static str>,
58+
font: KeyOrValue<ArcStr>,
5959
}
6060

6161
impl<T: Data> Label<T> {
@@ -141,7 +141,7 @@ impl<T: Data> Label<T> {
141141
/// The argument can be a `&str`, `String`, or [`Key<&str>`].
142142
///
143143
/// [`Key<&str>`]: ../struct.Key.html
144-
pub fn with_font(mut self, font: impl Into<KeyOrValue<&'static str>>) -> Self {
144+
pub fn with_font(mut self, font: impl Into<KeyOrValue<ArcStr>>) -> Self {
145145
self.font = font.into();
146146
self
147147
}
@@ -187,7 +187,7 @@ impl<T: Data> Label<T> {
187187
/// The argument can be a `&str`, `String`, or [`Key<&str>`].
188188
///
189189
/// [`Key<&str>`]: ../struct.Key.html
190-
pub fn set_font(&mut self, font: impl Into<KeyOrValue<&'static str>>) {
190+
pub fn set_font(&mut self, font: impl Into<KeyOrValue<ArcStr>>) {
191191
self.font = font.into();
192192
}
193193

@@ -198,7 +198,7 @@ impl<T: Data> Label<T> {
198198

199199
// TODO: caching of both the format and the layout
200200
self.text.with_display_text(|text| {
201-
let font = t.font_family(font_name).unwrap_or(FontFamily::SYSTEM_UI);
201+
let font = t.font_family(&font_name).unwrap_or(FontFamily::SYSTEM_UI);
202202
t.new_text_layout(&text)
203203
.font(font, font_size)
204204
.text_color(color.clone())

druid/src/widget/switch.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl Switch {
5959

6060
let font = ctx
6161
.text()
62-
.font_family(font_name)
62+
.font_family(&font_name)
6363
.unwrap_or(FontFamily::SYSTEM_UI);
6464

6565
// off/on labels

druid/src/widget/textbox.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ impl TextBox {
100100

101101
// TODO: caching of both the format and the layout
102102
let font = piet_text
103-
.font_family(font_name)
103+
.font_family(&font_name)
104104
.unwrap_or(FontFamily::SYSTEM_UI);
105105

106106
piet_text

0 commit comments

Comments
 (0)