summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/src/animation.rs12
-rw-r--r--core/src/element.rs5
-rw-r--r--core/src/input_method.rs54
-rw-r--r--core/src/length.rs6
-rw-r--r--core/src/lib.rs57
-rw-r--r--core/src/pixels.rs6
-rw-r--r--core/src/shell.rs2
-rw-r--r--core/src/widget/operation/focusable.rs27
8 files changed, 126 insertions, 43 deletions
diff --git a/core/src/animation.rs b/core/src/animation.rs
index 258fd084..14cbb5c3 100644
--- a/core/src/animation.rs
+++ b/core/src/animation.rs
@@ -13,6 +13,7 @@ where
T: Clone + Copy + PartialEq + Float,
{
raw: lilt::Animated<T, Instant>,
+ duration: Duration, // TODO: Expose duration getter in `lilt`
}
impl<T> Animation<T>
@@ -23,6 +24,7 @@ where
pub fn new(state: T) -> Self {
Self {
raw: lilt::Animated::new(state),
+ duration: Duration::from_millis(100),
}
}
@@ -58,6 +60,7 @@ where
/// Sets the duration of the [`Animation`] to the given value.
pub fn duration(mut self, duration: Duration) -> Self {
self.raw = self.raw.duration(duration.as_secs_f32() * 1_000.0);
+ self.duration = duration;
self
}
@@ -133,4 +136,13 @@ impl Animation<bool> {
{
self.raw.animate_bool(start, end, at)
}
+
+ /// Returns the remaining [`Duration`] of the [`Animation`].
+ pub fn remaining(&self, at: Instant) -> Duration {
+ Duration::from_secs_f32(self.interpolate(
+ self.duration.as_secs_f32(),
+ 0.0,
+ at,
+ ))
+ }
}
diff --git a/core/src/element.rs b/core/src/element.rs
index ede9e16c..b7d51aeb 100644
--- a/core/src/element.rs
+++ b/core/src/element.rs
@@ -93,6 +93,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> {
///
/// ```no_run
/// # mod iced {
+ /// # pub use iced_core::Function;
/// # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, ()>;
/// #
/// # pub mod widget {
@@ -119,7 +120,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> {
/// use counter::Counter;
///
/// use iced::widget::row;
- /// use iced::Element;
+ /// use iced::{Element, Function};
///
/// struct ManyCounters {
/// counters: Vec<Counter>,
@@ -142,7 +143,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> {
/// // Here we turn our `Element<counter::Message>` into
/// // an `Element<Message>` by combining the `index` and the
/// // message of the `element`.
- /// counter.map(move |message| Message::Counter(index, message))
+ /// counter.map(Message::Counter.with(index))
/// }),
/// )
/// .into()
diff --git a/core/src/input_method.rs b/core/src/input_method.rs
index 4e8c383b..cd8d459d 100644
--- a/core/src/input_method.rs
+++ b/core/src/input_method.rs
@@ -1,19 +1,15 @@
//! Listen to input method events.
-use crate::Point;
+use crate::{Pixels, Point};
use std::ops::Range;
/// The input method strategy of a widget.
#[derive(Debug, Clone, PartialEq)]
pub enum InputMethod<T = String> {
- /// No input method strategy has been specified.
- None,
- /// No input method is allowed.
+ /// Input method is disabled.
Disabled,
- /// Input methods are allowed, but not open yet.
- Allowed,
- /// Input method is open.
- Open {
+ /// Input method is enabled.
+ Enabled {
/// The position at which the input method dialog should be placed.
position: Point,
/// The [`Purpose`] of the input method.
@@ -34,6 +30,8 @@ pub struct Preedit<T = String> {
pub content: T,
/// The selected range of the content.
pub selection: Option<Range<usize>>,
+ /// The text size of the content.
+ pub text_size: Option<Pixels>,
}
impl<T> Preedit<T> {
@@ -53,6 +51,7 @@ impl<T> Preedit<T> {
Preedit {
content: self.content.as_ref().to_owned(),
selection: self.selection.clone(),
+ text_size: self.text_size,
}
}
}
@@ -63,6 +62,7 @@ impl Preedit {
Preedit {
content: &self.content,
selection: self.selection.clone(),
+ text_size: self.text_size,
}
}
}
@@ -87,26 +87,20 @@ impl InputMethod {
/// # use iced_core::input_method::{InputMethod, Purpose, Preedit};
/// # use iced_core::Point;
///
- /// let open = InputMethod::Open {
+ /// let open = InputMethod::Enabled {
/// position: Point::ORIGIN,
/// purpose: Purpose::Normal,
- /// preedit: Some(Preedit { content: "1".to_owned(), selection: None }),
+ /// preedit: Some(Preedit { content: "1".to_owned(), selection: None, text_size: None }),
/// };
///
- /// let open_2 = InputMethod::Open {
+ /// let open_2 = InputMethod::Enabled {
/// position: Point::ORIGIN,
/// purpose: Purpose::Secure,
- /// preedit: Some(Preedit { content: "2".to_owned(), selection: None }),
+ /// preedit: Some(Preedit { content: "2".to_owned(), selection: None, text_size: None }),
/// };
///
/// let mut ime = InputMethod::Disabled;
///
- /// ime.merge(&InputMethod::<String>::Allowed);
- /// assert_eq!(ime, InputMethod::Allowed);
- ///
- /// ime.merge(&InputMethod::<String>::Disabled);
- /// assert_eq!(ime, InputMethod::Allowed);
- ///
/// ime.merge(&open);
/// assert_eq!(ime, open);
///
@@ -114,22 +108,16 @@ impl InputMethod {
/// assert_eq!(ime, open);
/// ```
pub fn merge<T: AsRef<str>>(&mut self, other: &InputMethod<T>) {
- match (&self, other) {
- (InputMethod::Open { .. }, _)
- | (
- InputMethod::Allowed,
- InputMethod::None | InputMethod::Disabled,
- )
- | (InputMethod::Disabled, InputMethod::None) => {}
- _ => {
- *self = other.to_owned();
- }
+ if let InputMethod::Enabled { .. } = self {
+ return;
}
+
+ *self = other.to_owned();
}
/// Returns true if the [`InputMethod`] is open.
- pub fn is_open(&self) -> bool {
- matches!(self, Self::Open { .. })
+ pub fn is_enabled(&self) -> bool {
+ matches!(self, Self::Enabled { .. })
}
}
@@ -140,14 +128,12 @@ impl<T> InputMethod<T> {
T: AsRef<str>,
{
match self {
- Self::None => InputMethod::None,
Self::Disabled => InputMethod::Disabled,
- Self::Allowed => InputMethod::Allowed,
- Self::Open {
+ Self::Enabled {
position,
purpose,
preedit,
- } => InputMethod::Open {
+ } => InputMethod::Enabled {
position: *position,
purpose: *purpose,
preedit: preedit.as_ref().map(Preedit::to_owned),
diff --git a/core/src/length.rs b/core/src/length.rs
index 5f24169f..363833c4 100644
--- a/core/src/length.rs
+++ b/core/src/length.rs
@@ -77,8 +77,8 @@ impl From<f32> for Length {
}
}
-impl From<u16> for Length {
- fn from(units: u16) -> Self {
- Length::Fixed(f32::from(units))
+impl From<u32> for Length {
+ fn from(units: u32) -> Self {
+ Length::Fixed(units as f32)
}
}
diff --git a/core/src/lib.rs b/core/src/lib.rs
index d5c221ac..03cc0632 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -93,3 +93,60 @@ pub use smol_str::SmolStr;
pub fn never<T>(never: std::convert::Infallible) -> T {
match never {}
}
+
+/// A trait extension for binary functions (`Fn(A, B) -> O`).
+///
+/// It enables you to use a bunch of nifty functional programming paradigms
+/// that work well with iced.
+pub trait Function<A, B, O> {
+ /// Applies the given first argument to a binary function and returns
+ /// a new function that takes the other argument.
+ ///
+ /// This lets you partially "apply" a function—equivalent to currying,
+ /// but it only works with binary functions. If you want to apply an
+ /// arbitrary number of arguments, create a little struct for them.
+ ///
+ /// # When is this useful?
+ /// Sometimes you will want to identify the source or target
+ /// of some message in your user interface. This can be achieved through
+ /// normal means by defining a closure and moving the identifier
+ /// inside:
+ ///
+ /// ```rust
+ /// # let element: Option<()> = Some(());
+ /// # enum Message { ButtonPressed(u32, ()) }
+ /// let id = 123;
+ ///
+ /// # let _ = {
+ /// element.map(move |result| Message::ButtonPressed(id, result))
+ /// # };
+ /// ```
+ ///
+ /// That's quite a mouthful. [`with`](Self::with) lets you write:
+ ///
+ /// ```rust
+ /// # use iced_core::Function;
+ /// # let element: Option<()> = Some(());
+ /// # enum Message { ButtonPressed(u32, ()) }
+ /// let id = 123;
+ ///
+ /// # let _ = {
+ /// element.map(Message::ButtonPressed.with(id))
+ /// # };
+ /// ```
+ ///
+ /// Effectively creating the same closure that partially applies
+ /// the `id` to the message—but much more concise!
+ fn with(self, prefix: A) -> impl Fn(B) -> O;
+}
+
+impl<F, A, B, O> Function<A, B, O> for F
+where
+ F: Fn(A, B) -> O,
+ Self: Sized,
+ A: Copy,
+{
+ fn with(self, prefix: A) -> impl Fn(B) -> O {
+ move |result| self(prefix, result)
+ }
+}
diff --git a/core/src/pixels.rs b/core/src/pixels.rs
index 7d6267cf..c87e2b31 100644
--- a/core/src/pixels.rs
+++ b/core/src/pixels.rs
@@ -20,9 +20,9 @@ impl From<f32> for Pixels {
}
}
-impl From<u16> for Pixels {
- fn from(amount: u16) -> Self {
- Self(f32::from(amount))
+impl From<u32> for Pixels {
+ fn from(amount: u32) -> Self {
+ Self(amount as f32)
}
}
diff --git a/core/src/shell.rs b/core/src/shell.rs
index 509e3822..56250e2e 100644
--- a/core/src/shell.rs
+++ b/core/src/shell.rs
@@ -27,7 +27,7 @@ impl<'a, Message> Shell<'a, Message> {
redraw_request: window::RedrawRequest::Wait,
is_layout_invalid: false,
are_widgets_invalid: false,
- input_method: InputMethod::None,
+ input_method: InputMethod::Disabled,
}
}
diff --git a/core/src/widget/operation/focusable.rs b/core/src/widget/operation/focusable.rs
index 8f66e575..a1327bc1 100644
--- a/core/src/widget/operation/focusable.rs
+++ b/core/src/widget/operation/focusable.rs
@@ -61,6 +61,33 @@ pub fn focus<T>(target: Id) -> impl Operation<T> {
Focus { target }
}
+/// Produces an [`Operation`] that unfocuses the focused widget.
+pub fn unfocus<T>() -> impl Operation<T> {
+ struct Unfocus;
+
+ impl<T> Operation<T> for Unfocus {
+ fn focusable(
+ &mut self,
+ _id: Option<&Id>,
+ _bounds: Rectangle,
+ state: &mut dyn Focusable,
+ ) {
+ state.unfocus();
+ }
+
+ fn container(
+ &mut self,
+ _id: Option<&Id>,
+ _bounds: Rectangle,
+ operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
+ ) {
+ operate_on_children(self);
+ }
+ }
+
+ Unfocus
+}
+
/// Produces an [`Operation`] that generates a [`Count`] and chains it with the
/// provided function to build a new [`Operation`].
pub fn count() -> impl Operation<Count> {