//! Traits that are used in Alchemy. Alchemy implements a React-based Component //! lifecycle, coupled with a delegate pattern inspired by those found in AppKit/UIKit. use std::sync::Arc; use alchemy_styles::styles::{Layout, Style}; use crate::error::Error; use crate::rsx::{RSX, Props}; /// A per-platform wrapped Pointer type, used for attaching views/widgets. #[cfg(feature = "cocoa")] pub type PlatformSpecificNodeType = objc_id::ShareId; /// A per-platform wrapped Pointer type, used for attaching views/widgets. #[cfg(not(feature = "cocoa"))] pub type PlatformSpecificNodeType = (); /// Each platform tends to have their own startup routine, their own runloop, and so on. /// Alchemy recognizes this and provides an `AppDelegate` that receives events at a system /// level and allows the user to operate within the established framework per-system. pub trait AppDelegate: Send + Sync { /// Fired when an Application is about to finish launching. fn will_finish_launching(&mut self) {} /// Fired when an Application has finished launching - this is a good place to, say, show your /// window. fn did_finish_launching(&mut self) {} /// Fired when an Application will become active. fn will_become_active(&mut self) {} /// Fired when an Application became active. fn did_become_active(&mut self) {} /// Fired when an Application will resign active. You can use this to, say, persist resources /// or state. fn will_resign_active(&mut self) {} /// Fired when an Application has resigned active. fn did_resign_active(&mut self) {} /// Fired when an Application is going to terminate. You can use this to, say, instruct the /// system to "wait a minute, lemme finish". fn should_terminate(&self) -> bool { true } /// Fired when the Application has determined "no, you're done, stop the world". fn will_terminate(&mut self) {} /// A private trait method that you shouldn't call. This may change or disappear in later /// releases. Do not rely on this. fn _window_will_close(&self, _window_id: usize) {} } /// Each platform has their own `Window` API, which Alchemy attempts to pair down to one consistent /// API. This also acts as the bootstrapping point for a `render` tree. pub trait WindowDelegate: Send + Sync { /// Fired when this Window will close. You can use this to clean up or destroy resources, /// timers, and other things. fn will_close(&mut self) { } /// Called as the first step in the `render` tree. Every Window contains its own content view /// that is special, called the root. Widget trees are added to it as necessary, bootstrapped /// from here. fn render(&self) -> Result { Ok(RSX::None) } } pub trait State {} /// The `Component` lifecycle, mostly inspired from React, with a few extra methods for views that /// need to have a backing native layer. A good breakdown of the React Component lifecycle can be /// found [in this tweet](https://twitter.com/dan_abramov/status/981712092611989509?lang=en). /// /// Alchemy does not currently implement Hooks, and at the moment has no plans to do so (the API /// doesn't feel comfortable in Rust, in any way I tried). If you think you have an interesting /// proposal for this, feel free to open an issue! pub trait Component: Send + Sync { /// Indicates whether a Component instance carries a native backing node. If you return `true` /// from this, the reconciler will opt-in to the native backing layer. Returns `false` by /// default. fn has_native_backing_node(&self) -> bool { false } /// Returns a wrapped-per-platform pointer type that the backing framework tree can use. fn borrow_native_backing_node(&self) -> Option { None } /// If you implement a Native-backed component, you'll need to implement this. Given a /// `component`, you need to instruct the system how to append it to the tree at your point. fn append_child_component(&self, _component: &Arc) {} /// If you implement a Native-backed component, you'll need to implement this. Given a /// `component`, you need to instruct the system how to replace it in the tree at your point. fn replace_child_component(&self, _component: Arc) {} /// If you implement a Native-backed component, you'll need to implement this. Given a /// `component`, you need to instruct the system how to remove it from the tree at your point. fn remove_child_component(&self, _component: Arc) {} /// Given a computed `layout`, and an accompanying `Style` (which holds appearance-based /// styles, like colors), this method should transform them into appropriate calls to the /// backing native node. fn apply_styles(&self, _layout: &Layout, _style: &Style) {} /// Invoked right before calling the render method, both on the initial mount and on subsequent updates. /// It should return an object to update the state, or null to update nothing. /// This method exists for rare use cases where the state depends on changes in props over time. fn get_derived_state_from_props(&self, _props: Props) {} /// Invoked right before the most recently rendered output is committed to the backing layer tree. /// It enables your component to capture some information from the tree (e.g. scroll position) before it's /// potentially changed. Any value returned by this lifecycle will be passed as a parameter /// to component_did_update(). /// /// This use case is not common, but it may occur in UIs like a chat thread that need to handle scroll /// position in a special way. A snapshot value (or None) should be returned. fn get_snapshot_before_update(&self, _props: Props) {} /// Invoked immediately after a component is mounted (inserted into the tree). /// If you need to load data from a remote endpoint, this is a good place to instantiate the network request. /// This method is also a good place to set up any subscriptions. If you do that, don’t forget to unsubscribe /// in component_will_unmount(). fn component_did_mount(&mut self, _props: &Props) {} /// Invoked immediately after updating occurs. This method is not called for the initial render. /// This is also a good place to do network requests as long as you compare the current props to previous props /// (e.g. a network request may not be necessary if the props have not changed). fn component_did_update(&mut self, _props: &Props) {} /// Invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this /// method, such as invalidating timers, canceling network requests, or cleaning up any subscriptions that /// were created in component_did_mount(). /// /// You should not call set state in this method because the component will never be re-rendered. Once a /// component instance is unmounted, it will never be mounted again. fn component_will_unmount(&mut self, _props: &Props) {} /// Invoked after an error has been thrown by a descendant component. Called during the "commit" phase, /// so side-effects are permitted. It should be used for things like logging errors (e.g, /// Sentry). fn component_did_catch(&mut self, _props: &Props/* error: */) {} /// Use this to let Alchemy know if a component’s output is not affected by the current change in state /// or props. The default behavior is to re-render on every state change, and in the vast majority of /// cases you should rely on the default behavior. /// /// This is invoked before rendering when new props or state are being received. Defaults to true. This /// method is not called for the initial render or when force_update() is used. This method only exists /// as a performance optimization. Do not rely on it to “prevent” a rendering, as this can lead to bugs. fn should_component_update(&self) -> bool { true } /// The only required method for a `Component`. Should return a Result of RSX nodes, or an /// Error (in very rare cases, such as trying to get a key from a strange HashMap or /// something). /// /// The render() function should be pure, meaning that it does not modify component state, it /// returns the same result each time it’s invoked, and it does not directly interact with the /// backing rendering framework. /// /// If you need to interact with the browser, perform your work in component_did_mount() or the other /// lifecycle methods instead. Keeping `render()` pure makes components easier to think about. /// /// This method is not called if should_component_update() returns `false`. fn render(&self, _props: &Props) -> Result { Ok(RSX::None) } /// This lifecycle is invoked after an error has been thrown by a descendant component. It receives /// the error that was thrown as a parameter and should return a value to update state. /// /// This is called during the "render" phase, so side-effects are not permitted. /// For those use cases, use component_did_catch() instead. fn get_derived_state_from_error(&self, _error: ()) {} /// By default, when your component’s state or props change, your component will re-render. /// If your `render()` method depends on some other data, you can tell Alchemy that the component /// needs re-rendering by calling `force_update()`. /// /// Calling `force_update()` will cause `render()` to be called on the component, skipping /// `should_component_update()`. This will trigger the normal lifecycle methods for child components, /// including the `should_component_update()` method of each child. Alchemy will still only update the /// backing widget tree if the markup changes. /// /// Normally, you should try to avoid all uses of `force_update()` and only read from `this.props` /// and `this.state` in `render()`. fn force_update(&self) {} }