-
Notifications
You must be signed in to change notification settings - Fork 135
[WIP] Add Action associated type to Widget trait #939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super excited for this!
/// Trait alias for traits [`Debug`] and [`Any`]. | ||
/// | ||
/// Used to be able to declare `Box<dyn DebugAndAny>`. | ||
pub trait DebugAndAny: Debug + Any {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd consider calling this AnyAction
. That opens the door to future trait bounds without needing to rename all uses of this.
(That name suggestion is based on this being exactly the same idea as
xilem/xilem_core/src/message.rs
Lines 44 to 123 in 825cfd8
/// A simple dynamically typed message for the [`View`] trait. | |
/// | |
/// This is a thin wrapper around `Box<dyn Any>`, with added support for debug printing. | |
/// It is used as the default message type in Xilem Core. | |
/// The contained messages must also be [`Send`], which makes using this message type in a multithreaded context easier. | |
/// [`View`] is generic over the message type, in case this requirement is too restrictive. | |
/// Indeed, this functionality is used in Xilem Web. | |
/// | |
/// To convert a `DynMessage` into its concrete message type, you should use | |
/// [`downcast`](Self::downcast). | |
/// | |
/// This type is a struct rather than (say) a type alias, because type aliases are sometimes resolved by | |
/// rust-analyzer when autofilling a trait, which can also lead to buggy behaviour (we've previously seen | |
/// `Box<dyn Box<dyn Message>>` be generated). | |
/// | |
/// If the message contains sensitive data, make sure this isn't output in its `Debug` implementation, | |
/// as that may be called by the Xilem runtime (e.g. due to a bug meaning messages are redirected) or | |
/// any parent views. (That is, views do not need to design themselves as if the Debug implementation is ) | |
/// | |
/// [`View`]: crate::View | |
#[derive(Debug)] | |
pub struct DynMessage(pub Box<dyn AnyMessage>); | |
impl DynMessage { | |
/// Utility to make a `DynMessage` from a message value. | |
pub fn new(x: impl AnyMessage) -> Self { | |
Self(Box::new(x)) | |
} | |
/// Access the actual type of this [`DynMessage`]. | |
/// | |
/// ## Errors | |
/// | |
/// If the message contained within `self` is not of type `T`, returns `self` | |
/// (so that e.g. a different type can be used). | |
/// | |
/// In most cases, to handle this error, you will want to make an `error` log, | |
/// and return this as [`MessageResult::Stale`]; this case indicates that a parent | |
/// view has routed things incorrectly, but it's reasonable to be robust. | |
pub fn downcast<T: AnyMessage>(self) -> Result<Box<T>, Self> { | |
self.0.downcast().map_err(Self) | |
} | |
/// Returns `true` if the inner type is the same as `T`. | |
pub fn is<T: AnyMessage>(&self) -> bool { | |
self.0.is::<T>() | |
} | |
} | |
/// Types which can be used in [`DynMessage`] (and so can be the messages for Xilem views). | |
/// | |
/// The `Debug` requirement allows inspecting messages which were sent to the wrong place. | |
// TODO: Should/Could we remove the `Send` requirement here? | |
// It's not like implementing message handling in parallel is a meaningful operation. | |
// (If you need to send one, you can always use `dyn AnyMessage + Send`) | |
// Making that change would mean we could make View no longer generic over the message type again. | |
pub trait AnyMessage: Any + Debug + Send {} | |
impl<T> AnyMessage for T where T: Any + Debug + Send {} | |
impl dyn AnyMessage { | |
/// Access the actual type of this [`DynMessage`]. | |
/// | |
/// ## Errors | |
/// | |
/// If the message contained within `self` is not of type `T`, returns `self` | |
/// (so that e.g. a different type can be used) | |
pub fn downcast<T: AnyMessage>(self: Box<Self>) -> Result<Box<T>, Box<Self>> { | |
if self.is::<T>() { | |
Ok((self as Box<dyn Any>).downcast::<T>().unwrap()) | |
} else { | |
Err(self) | |
} | |
} | |
/// Returns `true` if the inner type is the same as `T`. | |
pub fn is<T: AnyMessage>(&self) -> bool { | |
let this: &dyn Any = self; | |
this.is::<T>() | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the point where I saw AnyMessage was when I decided to just commit what I had and stop.
My plan is to create a clean slate PR that moves AnyMessage to Masonry, and when it's merged, rebase this PR on top of it.
// TODO - TextCursor changed, ImeChanged, EnterKey, MouseEnter | ||
#[non_exhaustive] | ||
/// Events from UI elements. | ||
/// | ||
/// Note: Actions are still a WIP feature. | ||
pub enum Action { | ||
pub enum DefaultAction { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document that this is an intermediate state. I think for reviewing, it wouldn't be unreasonable to leave this as-is, and then split it out as a follow-up
For the record, this is extremely incomplete code (I just committed what I had when I stopped working) and it's not super high-priority, so I might leave it as it is for a while. |
No description provided.