Merge pull request #272 from DioxusLabs/jk/documet-everything

docs: add `deny(missing_docs)` is many more places
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2022-02-26 17:46:30 -05:00
commit 2181ccd80f
19 files changed
+398 -185

No files matched your search

+2 -2
View File
@@ -79,7 +79,7 @@ impl ToTokens for CallBody {
match &self.custom_context {
// The `in cx` pattern allows directly rendering
Some(ident) => out_tokens.append_all(quote! {
#ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
#ident.render(LazyNodes::new(move |__cx: NodeFactory| -> VNode {
use dioxus_elements::{GlobalAttributes, SvgAttributes};
#inner
}))
@@ -87,7 +87,7 @@ impl ToTokens for CallBody {
// Otherwise we just build the LazyNode wrapper
None => out_tokens.append_all(quote! {
LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
LazyNodes::new(move |__cx: NodeFactory| -> VNode {
use dioxus_elements::{GlobalAttributes, SvgAttributes};
#inner
})
+1 -9
View File
@@ -257,11 +257,10 @@ impl<'b> DiffState<'b> {
vcomponent.scope.set(Some(new_idx));
log::trace!(
"created component \"{}\", id: {:?} parent {:?} orig: {:?}",
"created component \"{}\", id: {:?} parent {:?}",
vcomponent.fn_name,
new_idx,
parent_idx,
vcomponent.originator
);
// if vcomponent.can_memoize {
@@ -957,13 +956,6 @@ impl<'b> DiffState<'b> {
let props = scope.props.take().unwrap();
c.props.borrow_mut().replace(props);
self.scopes.try_remove(scope_id).unwrap();
// // we can only remove components if they are actively being diffed
// if self.scope_stack.contains(&c.originator) {
// log::trace!("Removing component {:?}", old);
// self.scopes.try_remove(scope_id).unwrap();
// }
}
self.leave_scope();
}
+22 -5
View File
@@ -119,24 +119,41 @@ pub enum EventPriority {
Low = 0,
}
/// The internal Dioxus type that carries any event data to the relevant handler.
///
///
///
///
///
pub struct AnyEvent {
pub(crate) bubble_state: Rc<BubbleState>,
pub(crate) data: Arc<dyn Any + Send + Sync>,
}
impl AnyEvent {
/// Convert this AnyEvent into a specific UiEvent with EventData.
///
/// ```rust, ignore
/// let evt: FormEvent = evvt.downcast().unwrap();
/// ```
pub fn downcast<T: Send + Sync + 'static>(self) -> Option<UiEvent<T>> {
let AnyEvent { data, bubble_state } = self;
if let Ok(data) = data.downcast::<T>() {
Some(UiEvent { bubble_state, data })
} else {
None
}
data.downcast::<T>()
.ok()
.map(|data| UiEvent { bubble_state, data })
}
}
/// A UiEvent is a type that wraps various EventData.
///
/// You should prefer to use the name of the event directly, rather than
/// the UiEvent<T> generic type.
///
/// For the HTML crate, this would include [`MouseEvent`], [`FormEvent`] etc.
pub struct UiEvent<T> {
/// The internal data of the event
/// This is wrapped in an Arc so that it can be sent across threads
pub data: Arc<T>,
#[allow(unused)]
+36 -30
View File
@@ -35,14 +35,32 @@ enum StackNodeStorage<'a, 'b> {
}
impl<'a, 'b> LazyNodes<'a, 'b> {
pub fn new_some<F>(_val: F) -> Self
where
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
{
Self::new(_val)
/// Create a new LazyNodes closure, optimistically placing it onto the stack.
///
/// If the closure cannot fit into the stack allocation (16 bytes), then it
/// is placed on the heap. Most closures will fit into the stack, and is
/// the most optimal way to use the creation function.
pub fn new(val: impl FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b) -> Self {
// there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
let mut slot = Some(val);
let val = move |fac: Option<NodeFactory<'a>>| {
let inner = slot.take().unwrap();
fac.map(inner)
};
// miri does not know how to work with mucking directly into bytes
// just use a heap allocated type when miri is running
if cfg!(miri) {
Self {
inner: StackNodeStorage::Heap(Box::new(val)),
}
} else {
unsafe { LazyNodes::new_inner(val) }
}
}
/// force this call onto the stack
/// Create a new LazyNodes closure, but force it onto the heap.
pub fn new_boxed<F>(_val: F) -> Self
where
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
@@ -60,25 +78,6 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
}
}
pub fn new(_val: impl FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b) -> Self {
// there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
let mut slot = Some(_val);
let val = move |fac: Option<NodeFactory<'a>>| {
let inner = slot.take().unwrap();
fac.map(inner)
};
// miri does not know how to work with mucking directly into bytes
if cfg!(miri) {
Self {
inner: StackNodeStorage::Heap(Box::new(val)),
}
} else {
unsafe { LazyNodes::new_inner(val) }
}
}
unsafe fn new_inner<F>(val: F) -> Self
where
F: FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b,
@@ -153,6 +152,15 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
}
}
/// Call the closure with the given factory to produce real VNodes.
///
/// ```rust, ignore
/// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
///
/// let fac = NodeFactory::new(&cx);
///
/// let node = f.call(cac);
/// ```
pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
match self.inner {
StackNodeStorage::Heap(mut lazy) => lazy(Some(f)).unwrap(),
@@ -245,9 +253,7 @@ mod tests {
#[test]
fn it_works() {
fn app(cx: Scope<()>) -> Element {
cx.render(LazyNodes::new_some(|f| {
f.text(format_args!("hello world!"))
}))
cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!"))))
}
let mut dom = VirtualDom::new(app);
@@ -280,7 +286,7 @@ mod tests {
.map(|i| {
let val = cx.props.inner.clone();
LazyNodes::new_some(move |f| {
LazyNodes::new(move |f| {
log::debug!("hell closure");
let inner = DropInner { id: i };
f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
@@ -288,7 +294,7 @@ mod tests {
})
.collect::<Vec<_>>();
LazyNodes::new_some(|f| {
LazyNodes::new(|f| {
log::debug!("main closure");
f.fragment_from_iter(it)
})
+4
View File
@@ -1,5 +1,6 @@
#![allow(non_snake_case)]
#![doc = include_str!("../README.md")]
#![deny(missing_docs)]
pub(crate) mod diff;
pub(crate) mod events;
@@ -76,6 +77,9 @@ pub use crate::innerlude::{
VElement, VFragment, VNode, VPlaceholder, VText, VirtualDom,
};
/// The purpose of this module is to alleviate imports of many common types
///
/// This includes types like [`Scope`], [`Element`], and [`Component`].
pub mod prelude {
pub use crate::innerlude::{
fc_to_builder, Attributes, Component, DioxusElement, Element, EventHandler, Fragment,
+86 -10
View File
@@ -16,8 +16,13 @@ use std::{any::Any, fmt::Debug};
///
/// Mutations are the only link between the RealDOM and the VirtualDOM.
pub struct Mutations<'a> {
/// The list of edits that need to be applied for the RealDOM to match the VirtualDOM.
pub edits: Vec<DomEdit<'a>>,
/// The list of Scopes that were diffed, created, and removed during the Diff process.
pub dirty_scopes: FxHashSet<ScopeId>,
/// The list of nodes to connect to the RealDOM.
pub refs: Vec<NodeRefMutation<'a>>,
}
@@ -39,74 +44,145 @@ impl Debug for Mutations<'_> {
serde(tag = "type")
)]
pub enum DomEdit<'bump> {
/// Push the given root node onto our stack.
PushRoot {
/// The ID of the root node to push.
root: u64,
},
/// Pop the topmost node from our stack and append them to the node
/// at the top of the stack.
AppendChildren {
/// How many nodes should be popped from the stack.
/// The node remaining on the stack will be the target for the append.
many: u32,
},
// // save a possibly-fragment node as a template
// SaveAsTemplate {
// many: u32,
// },
// "Root" refers to the item directly
// it's a waste of an instruction to push the root directly
/// Replace a given (single) node with a handful of nodes currently on the stack.
ReplaceWith {
/// The ID of the node to be replaced.
root: u64,
/// How many nodes should be popped from the stack to replace the target node.
m: u32,
},
/// Insert a number of nodes after a given node.
InsertAfter {
/// The ID of the node to insert after.
root: u64,
/// How many nodes should be popped from the stack to insert after the target node.
n: u32,
},
/// Insert a number of nodes before a given node.
InsertBefore {
/// The ID of the node to insert before.
root: u64,
/// How many nodes should be popped from the stack to insert before the target node.
n: u32,
},
/// Remove a particular node from the DOM
Remove {
/// The ID of the node to remove.
root: u64,
},
/// Create a new purely-text node
CreateTextNode {
/// The ID the new node should have.
root: u64,
/// The textcontent of the node
text: &'bump str,
root: u64,
},
/// Create a new purely-element node
CreateElement {
tag: &'bump str,
/// The ID the new node should have.
root: u64,
/// The tagname of the node
tag: &'bump str,
},
/// Create a new purely-comment node with a given namespace
CreateElementNs {
tag: &'bump str,
/// The ID the new node should have.
root: u64,
/// The namespace of the node
tag: &'bump str,
/// The namespace of the node (like `SVG`)
ns: &'static str,
},
/// Create a new placeholder node.
/// In most implementations, this will either be a hidden div or a comment node.
CreatePlaceholder {
/// The ID the new node should have.
root: u64,
},
/// Create a new Event Listener.
NewEventListener {
/// The name of the event to listen for.
event_name: &'static str,
/// The ID of the node to attach the listener to.
scope: ScopeId,
/// The ID of the node to attach the listener to.
root: u64,
},
/// Remove an existing Event Listener.
RemoveEventListener {
/// The ID of the node to remove.
root: u64,
/// The name of the event to remove.
event: &'static str,
},
/// Set the textcontent of a node.
SetText {
/// The ID of the node to set the textcontent of.
root: u64,
/// The textcontent of the node
text: &'bump str,
},
/// Set the value of a node's attribute.
SetAttribute {
/// The ID of the node to set the attribute of.
root: u64,
/// The name of the attribute to set.
field: &'static str,
/// The value of the attribute.
value: &'bump str,
/// The (optional) namespace of the attribute.
/// For instance, "style" is in the "style" namespace.
ns: Option<&'bump str>,
},
/// Remove an attribute from a node.
RemoveAttribute {
/// The ID of the node to remove.
root: u64,
/// The name of the attribute to remove.
name: &'static str,
/// The namespace of the attribute.
ns: Option<&'bump str>,
},
}
+81 -14
View File
@@ -148,7 +148,7 @@ impl<'src> VNode<'src> {
}
// Create an "owned" version of the vnode.
pub fn decouple(&self) -> VNode<'src> {
pub(crate) fn decouple(&self) -> VNode<'src> {
match *self {
VNode::Text(t) => VNode::Text(t),
VNode::Element(e) => VNode::Element(e),
@@ -181,7 +181,6 @@ impl Debug for VNode<'_> {
.field("fnptr", &comp.user_fc)
.field("key", &comp.key)
.field("scope", &comp.scope)
.field("originator", &comp.originator)
.finish(),
}
}
@@ -200,6 +199,7 @@ impl std::fmt::Display for ElementId {
}
impl ElementId {
/// Convertt the ElementId to a `u64`.
pub fn as_u64(self) -> u64 {
self.0 as u64
}
@@ -211,18 +211,26 @@ fn empty_cell() -> Cell<Option<ElementId>> {
/// A placeholder node only generated when Fragments don't have any children.
pub struct VPlaceholder {
/// The [`ElementId`] of the placeholder.
pub id: Cell<Option<ElementId>>,
}
/// A bump-allocated string slice and metadata.
pub struct VText<'src> {
pub text: &'src str,
/// The [`ElementId`] of the VText.
pub id: Cell<Option<ElementId>>,
/// The text of the VText.
pub text: &'src str,
/// An indiciation if this VText can be ignored during diffing
/// Is usually only when there are no strings to be formatted (so the text is &'static str)
pub is_static: bool,
}
/// A list of VNodes with no single root.
pub struct VFragment<'src> {
/// The key of the fragment to be used during keyed diffing.
pub key: Option<&'src str>,
/// Fragments can never have zero children. Enforced by NodeFactory.
@@ -233,13 +241,34 @@ pub struct VFragment<'src> {
/// An element like a "div" with children, listeners, and attributes.
pub struct VElement<'a> {
pub tag: &'static str,
pub namespace: Option<&'static str>,
pub key: Option<&'a str>,
/// The [`ElementId`] of the VText.
pub id: Cell<Option<ElementId>>,
/// The key of the element to be used during keyed diffing.
pub key: Option<&'a str>,
/// The tag name of the element.
///
/// IE "div"
pub tag: &'static str,
/// The namespace of the VElement
///
/// IE "svg"
pub namespace: Option<&'static str>,
/// The parent of the Element (if any).
///
/// Used when bubbling events
pub parent: Cell<Option<ElementId>>,
/// The Listeners of the VElement.
pub listeners: &'a [Listener<'a>],
/// The attributes of the VElement.
pub attributes: &'a [Attribute<'a>],
/// The children of the VElement.
pub children: &'a [VNode<'a>],
}
@@ -275,12 +304,19 @@ impl Debug for VElement<'_> {
/// };
/// ```
pub trait DioxusElement {
/// The tag name of the element.
const TAG_NAME: &'static str;
/// The namespace of the element.
const NAME_SPACE: Option<&'static str>;
/// The tag name of the element.
#[inline]
fn tag_name(&self) -> &'static str {
Self::TAG_NAME
}
/// The namespace of the element.
#[inline]
fn namespace(&self) -> Option<&'static str> {
Self::NAME_SPACE
@@ -291,16 +327,25 @@ pub trait DioxusElement {
/// `href="https://example.com"`.
#[derive(Clone, Debug)]
pub struct Attribute<'a> {
/// The name of the attribute.
pub name: &'static str,
/// The value of the attribute.
pub value: &'a str,
/// An indication if this attribute can be ignored during diffing
///
/// Usually only when there are no strings to be formatted (so the value is &'static str)
pub is_static: bool,
/// An indication of we should always try and set the attribute.
/// Used in controlled components to ensure changes are propagated.
pub is_volatile: bool,
// Doesn't exist in the html spec.
// Used in Dioxus to denote "style" tags.
/// The namespace of the attribute.
///
/// Doesn't exist in the html spec.
/// Used in Dioxus to denote "style" tags and other attribute groups.
pub namespace: Option<&'static str>,
}
@@ -353,6 +398,8 @@ type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
///
/// ```
pub struct EventHandler<'bump, T = ()> {
/// The (optional) callback that the user specified
/// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
}
@@ -381,12 +428,23 @@ impl<T> EventHandler<'_, T> {
/// Virtual Components for custom user-defined components
/// Only supports the functional syntax
pub struct VComponent<'src> {
/// The key of the component to be used during keyed diffing.
pub key: Option<&'src str>,
pub originator: ScopeId,
/// The ID of the component.
/// Will not be assigned until after the component has been initialized.
pub scope: Cell<Option<ScopeId>>,
/// An indication if the component is static (can be memozied)
pub can_memoize: bool,
/// The function pointer to the component's render function.
pub user_fc: ComponentPtr,
/// The actual name of the component.
pub fn_name: &'static str,
/// The props of the component.
pub props: RefCell<Option<Box<dyn AnyProps + 'src>>>,
}
@@ -434,6 +492,7 @@ pub struct NodeFactory<'a> {
}
impl<'a> NodeFactory<'a> {
/// Create a new [`NodeFactory`] from a [`Scope`] or [`ScopeState`]
pub fn new(scope: &'a ScopeState) -> NodeFactory<'a> {
NodeFactory {
scope,
@@ -441,6 +500,7 @@ impl<'a> NodeFactory<'a> {
}
}
/// Get the custom alloactor for this component
#[inline]
pub fn bump(&self) -> &'a bumpalo::Bump {
self.bump
@@ -482,6 +542,7 @@ impl<'a> NodeFactory<'a> {
}))
}
/// Create a new [`VNode::VElement`]
pub fn element(
&self,
el: impl DioxusElement,
@@ -500,6 +561,9 @@ impl<'a> NodeFactory<'a> {
)
}
/// Create a new [`VNode::VElement`] without the trait bound
///
/// IE pass in "div" instead of `div`
pub fn raw_element(
&self,
tag_name: &'static str,
@@ -529,6 +593,7 @@ impl<'a> NodeFactory<'a> {
}))
}
/// Create a new [`Attribute`]
pub fn attr(
&self,
name: &'static str,
@@ -546,6 +611,7 @@ impl<'a> NodeFactory<'a> {
}
}
/// Create a new [`VNode::VComponent`]
pub fn component<P>(
&self,
component: fn(Scope<'a, P>) -> Element,
@@ -561,11 +627,8 @@ impl<'a> NodeFactory<'a> {
scope: Default::default(),
can_memoize: P::IS_STATIC,
user_fc: component as ComponentPtr,
originator: self.scope.scope_id(),
fn_name,
props: RefCell::new(Some(Box::new(VComponentProps {
// local_props: RefCell::new(Some(props)),
// heap_props: RefCell::new(None),
props,
memo: P::memoize, // smuggle the memoization function across borders
@@ -586,6 +649,7 @@ impl<'a> NodeFactory<'a> {
VNode::Component(vcomp)
}
/// Create a new [`Listener`]
pub fn listener(self, event: &'static str, callback: InternalHandler<'a>) -> Listener<'a> {
Listener {
event,
@@ -594,6 +658,7 @@ impl<'a> NodeFactory<'a> {
}
}
/// Create a new [`VNode::VFragment`] from a root of the rsx! call
pub fn fragment_root<'b, 'c>(
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
@@ -614,6 +679,7 @@ impl<'a> NodeFactory<'a> {
}
}
/// Create a new [`VNode::VFragment`] from any iterator
pub fn fragment_from_iter<'b, 'c>(
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
@@ -653,8 +719,7 @@ impl<'a> NodeFactory<'a> {
}
}
// this isn't quite feasible yet
// I think we need some form of interior mutability or state on nodefactory that stores which subtree was created
/// Create a new [`VNode`] from any iterator of children
pub fn create_children(
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
@@ -679,6 +744,7 @@ impl<'a> NodeFactory<'a> {
}
}
/// Create a new [`EventHandler`] from an [`FnMut`]
pub fn event_handler<T>(self, f: impl FnMut(T) + 'a) -> EventHandler<'a, T> {
let handler: &mut dyn FnMut(T) = self.bump.alloc(f);
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
@@ -709,6 +775,7 @@ impl Debug for NodeFactory<'_> {
/// As such, all node creation must go through the factory, which is only available in the component context.
/// These strict requirements make it possible to manage lifetimes and state.
pub trait IntoVNode<'a> {
/// Convert this into a [`VNode`], using the [`NodeFactory`] as a source of allocation
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
}
+6
View File
@@ -128,8 +128,14 @@ pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
/// }
/// ```
pub trait Properties: Sized {
/// The type of the builder for this component.
/// Used to create "in-progress" versions of the props.
type Builder;
/// An indication if these props are can be memoized automatically.
const IS_STATIC: bool;
/// Create a builder for this component.
fn builder() -> Self::Builder;
/// Memoization can only happen if the props are valid for the 'static lifetime
+5 -1
View File
@@ -396,7 +396,10 @@ impl ScopeArena {
/// }
/// ```
pub struct Scope<'a, P = ()> {
/// The internal ScopeState for this component
pub scope: &'a ScopeState,
/// The props for this component
pub props: &'a P,
}
@@ -750,7 +753,8 @@ impl ScopeState {
self.push_future(fut);
}
// todo: attach some state to the future to know if we should poll it
/// Informs the scheduler that this task is no longer needed and should be removed
/// on next poll.
pub fn remove_future(&self, id: TaskId) {
self.tasks.remove_fut(id);
}
+5
View File
@@ -1,5 +1,7 @@
use crate::innerlude::*;
/// An iterator that only yields "real" [`Element`]s. IE only Elements that are
/// not [`VNode::VComponent`] or [`VNode::VFragment`], .
pub struct ElementIdIterator<'a> {
vdom: &'a VirtualDom,
@@ -9,6 +11,9 @@ pub struct ElementIdIterator<'a> {
}
impl<'a> ElementIdIterator<'a> {
/// Create a new iterator from the given [`VirtualDom`] and [`VNode`]
///
/// This will allow you to iterate through all the real childrne of the [`VNode`].
pub fn new(vdom: &'a VirtualDom, node: &'a VNode<'a>) -> Self {
Self {
vdom,
+9 -3
View File
@@ -114,15 +114,18 @@ pub struct VirtualDom {
),
}
/// The type of message that can be sent to the scheduler.
///
/// These messages control how the scheduler will process updates to the UI.
#[derive(Debug)]
pub enum SchedulerMsg {
// events from the host
/// Events from the Renderer
Event(UserEvent),
// setstate
/// Immediate updates from Components that mark them as dirty
Immediate(ScopeId),
// an async task pushed from an event handler (or just spawned)
/// New tasks from components that should be polled when the next poll is ready
NewTask(ScopeId),
}
@@ -388,6 +391,9 @@ impl VirtualDom {
}
}
/// Handle an individual message for the scheduler.
///
/// This will either call an event listener or mark a component as dirty.
pub fn process_message(&mut self, msg: SchedulerMsg) {
match msg {
SchedulerMsg::NewTask(_id) => {
+1 -2
View File
@@ -1,5 +1,4 @@
use crate::desktop_context::DesktopContext;
use crate::user_window_events::UserWindowEvent;
use crate::desktop_context::{DesktopContext, UserWindowEvent};
use dioxus_core::*;
use std::{
collections::{HashMap, VecDeque},
+87 -12
View File
@@ -3,14 +3,23 @@ use std::rc::Rc;
use dioxus_core::ScopeState;
use wry::application::event_loop::EventLoopProxy;
use crate::user_window_events::UserWindowEvent;
use UserWindowEvent::*;
type ProxyType = EventLoopProxy<UserWindowEvent>;
pub type ProxyType = EventLoopProxy<UserWindowEvent>;
/// Desktop-Window handle api context
/// Get an imperative handle to the current window
pub fn use_window(cx: &ScopeState) -> &Rc<DesktopContext> {
cx.use_hook(|_| cx.consume_context::<DesktopContext>())
.as_ref()
.unwrap()
}
/// An imperative interface to the current window.
///
/// you can use this context control some window event
/// To get a handle to the current window, use the [`use_window`] hook.
///
///
/// # Example
///
/// you can use `cx.consume_context::<DesktopContext>` to get this context
///
@@ -19,7 +28,8 @@ type ProxyType = EventLoopProxy<UserWindowEvent>;
/// ```
#[derive(Clone)]
pub struct DesktopContext {
proxy: ProxyType,
/// The wry/tao proxy to the current window
pub proxy: ProxyType,
}
impl DesktopContext {
@@ -84,12 +94,12 @@ impl DesktopContext {
let _ = self.proxy.send_event(AlwaysOnTop(top));
}
// set cursor visible or not
/// set cursor visible or not
pub fn set_cursor_visible(&self, visible: bool) {
let _ = self.proxy.send_event(CursorVisible(visible));
}
// set cursor grab
/// set cursor grab
pub fn set_cursor_grab(&self, grab: bool) {
let _ = self.proxy.send_event(CursorGrab(grab));
}
@@ -110,9 +120,74 @@ impl DesktopContext {
}
}
/// use this function can get the `DesktopContext` context.
pub fn use_window(cx: &ScopeState) -> &Rc<DesktopContext> {
cx.use_hook(|_| cx.consume_context::<DesktopContext>())
.as_ref()
.unwrap()
use wry::application::event_loop::ControlFlow;
use wry::application::window::Fullscreen as WryFullscreen;
use crate::controller::DesktopController;
#[derive(Debug)]
pub enum UserWindowEvent {
Update,
CloseWindow,
DragWindow,
FocusWindow,
Visible(bool),
Minimize(bool),
Maximize(bool),
MaximizeToggle,
Resizable(bool),
AlwaysOnTop(bool),
Fullscreen(bool),
CursorVisible(bool),
CursorGrab(bool),
SetTitle(String),
SetDecorations(bool),
DevTool,
}
pub(super) fn handler(
user_event: UserWindowEvent,
desktop: &mut DesktopController,
control_flow: &mut ControlFlow,
) {
// currently dioxus-desktop supports a single window only,
// so we can grab the only webview from the map;
let webview = desktop.webviews.values().next().unwrap();
let window = webview.window();
match user_event {
Update => desktop.try_load_ready_webviews(),
CloseWindow => *control_flow = ControlFlow::Exit,
DragWindow => {
// if the drag_window has any errors, we don't do anything
window.fullscreen().is_none().then(|| window.drag_window());
}
Visible(state) => window.set_visible(state),
Minimize(state) => window.set_minimized(state),
Maximize(state) => window.set_maximized(state),
MaximizeToggle => window.set_maximized(!window.is_maximized()),
Fullscreen(state) => {
if let Some(handle) = window.current_monitor() {
window.set_fullscreen(state.then(|| WryFullscreen::Borderless(Some(handle))));
}
}
FocusWindow => window.set_focus(),
Resizable(state) => window.set_resizable(state),
AlwaysOnTop(state) => window.set_always_on_top(state),
CursorVisible(state) => window.set_cursor_visible(state),
CursorGrab(state) => {
let _ = window.set_cursor_grab(state);
}
SetTitle(content) => window.set_title(&content),
SetDecorations(state) => window.set_decorations(state),
DevTool => webview.devtool(),
}
}
+13 -13
View File
@@ -1,18 +1,23 @@
#![doc = include_str!("readme.md")]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
#![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
#![deny(missing_docs)]
pub mod cfg;
mod cfg;
mod controller;
pub mod desktop_context;
pub mod escape;
pub mod events;
mod desktop_context;
mod escape;
mod events;
mod protocol;
mod user_window_events;
use desktop_context::UserWindowEvent;
pub use desktop_context::{use_window, DesktopContext};
pub use wry;
pub use wry::application as tao;
use crate::events::trigger_from_serialized;
use cfg::DesktopConfig;
use controller::DesktopController;
pub use desktop_context::use_window;
use dioxus_core::*;
use events::parse_ipc_message;
use tao::{
@@ -20,12 +25,8 @@ use tao::{
event_loop::{ControlFlow, EventLoop},
window::Window,
};
pub use wry;
pub use wry::application as tao;
use wry::webview::WebViewBuilder;
use crate::events::trigger_from_serialized;
/// Launch the WebView and run the event loop.
///
/// This function will start a multithreaded Tokio runtime as well the WebView event loop.
@@ -142,8 +143,7 @@ pub fn launch_with_props<P: 'static + Send>(
}
"initialize" => {
is_ready.store(true, std::sync::atomic::Ordering::Relaxed);
let _ = proxy
.send_event(user_window_events::UserWindowEvent::Update);
let _ = proxy.send_event(UserWindowEvent::Update);
}
"browser_open" => {
let data = message.params();
@@ -218,7 +218,7 @@ pub fn launch_with_props<P: 'static + Send>(
},
Event::UserEvent(user_event) => {
user_window_events::handler(user_event, &mut desktop, control_flow)
desktop_context::handler(user_event, &mut desktop, control_flow)
}
Event::MainEventsCleared => {}
Event::Resumed => {}
@@ -1,73 +0,0 @@
use wry::application::event_loop::ControlFlow;
use wry::application::window::Fullscreen as WryFullscreen;
use crate::controller::DesktopController;
#[derive(Debug)]
pub(crate) enum UserWindowEvent {
Update,
CloseWindow,
DragWindow,
FocusWindow,
Visible(bool),
Minimize(bool),
Maximize(bool),
MaximizeToggle,
Resizable(bool),
AlwaysOnTop(bool),
Fullscreen(bool),
CursorVisible(bool),
CursorGrab(bool),
SetTitle(String),
SetDecorations(bool),
DevTool,
}
use UserWindowEvent::*;
pub(super) fn handler(
user_event: UserWindowEvent,
desktop: &mut DesktopController,
control_flow: &mut ControlFlow,
) {
// currently dioxus-desktop supports a single window only,
// so we can grab the only webview from the map;
let webview = desktop.webviews.values().next().unwrap();
let window = webview.window();
match user_event {
Update => desktop.try_load_ready_webviews(),
CloseWindow => *control_flow = ControlFlow::Exit,
DragWindow => {
// if the drag_window has any errors, we don't do anything
window.fullscreen().is_none().then(|| window.drag_window());
}
Visible(state) => window.set_visible(state),
Minimize(state) => window.set_minimized(state),
Maximize(state) => window.set_maximized(state),
MaximizeToggle => window.set_maximized(!window.is_maximized()),
Fullscreen(state) => {
if let Some(handle) = window.current_monitor() {
window.set_fullscreen(state.then(|| WryFullscreen::Borderless(Some(handle))));
}
}
FocusWindow => window.set_focus(),
Resizable(state) => window.set_resizable(state),
AlwaysOnTop(state) => window.set_always_on_top(state),
CursorVisible(state) => window.set_cursor_visible(state),
CursorGrab(state) => {
let _ = window.set_cursor_grab(state);
}
SetTitle(content) => window.set_title(&content),
SetDecorations(state) => window.set_decorations(state),
DevTool => webview.devtool(),
}
}
+3
View File
@@ -1,3 +1,6 @@
// #![deny(missing_docs)]
//! Useful foundational hooks for Dioxus
mod usestate;
pub use usestate::{use_state, UseState};
+33 -9
View File
@@ -1,19 +1,30 @@
#![allow(missing_docs)]
use dioxus_core::{ScopeState, TaskId};
use std::{cell::Cell, future::Future, rc::Rc, sync::Arc};
/// A future that resolves to a value.
///
/// This runs the future only once - though the future may be regenerated
/// through the [`UseFuture::restart`] method.
///
/// This is commonly used for components that cannot be rendered until some
/// asynchronous operation has completed.
///
///
///
///
///
pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
cx: &'a ScopeState,
new_fut: impl FnOnce() -> F,
) -> &'a UseFuture<T> {
let state = cx.use_hook(move |_| {
//
UseFuture {
update: cx.schedule_update(),
needs_regen: Cell::new(true),
slot: Rc::new(Cell::new(None)),
value: None,
task: None,
}
let state = cx.use_hook(move |_| UseFuture {
update: cx.schedule_update(),
needs_regen: Cell::new(true),
slot: Rc::new(Cell::new(None)),
value: None,
task: None,
pending: true,
});
if let Some(value) = state.slot.take() {
@@ -24,6 +35,7 @@ pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
if state.needs_regen.get() {
// We don't need regen anymore
state.needs_regen.set(false);
state.pending = false;
// Create the new future
let fut = new_fut();
@@ -42,10 +54,17 @@ pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
state
}
pub enum FutureState<'a, T> {
Pending,
Complete(&'a T),
Regenerating(&'a T), // the old value
}
pub struct UseFuture<T> {
update: Arc<dyn Fn()>,
needs_regen: Cell<bool>,
value: Option<T>,
pending: bool,
slot: Rc<Cell<Option<T>>>,
task: Option<TaskId>,
}
@@ -72,4 +91,9 @@ impl<T> UseFuture<T> {
pub fn value(&self) -> Option<&T> {
self.value.as_ref()
}
pub fn state(&self) -> FutureState<T> {
// self.value.as_ref()
FutureState::Pending
}
}
+2 -2
View File
@@ -79,7 +79,7 @@ impl ToTokens for CallBody {
match &self.custom_context {
// The `in cx` pattern allows directly rendering
Some(ident) => out_tokens.append_all(quote! {
#ident.render(LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
#ident.render(LazyNodes::new(move |__cx: NodeFactory| -> VNode {
use dioxus_elements::{GlobalAttributes, SvgAttributes};
#inner
}))
@@ -87,7 +87,7 @@ impl ToTokens for CallBody {
// Otherwise we just build the LazyNode wrapper
None => out_tokens.append_all(quote! {
LazyNodes::new_some(move |__cx: NodeFactory| -> VNode {
LazyNodes::new(move |__cx: NodeFactory| -> VNode {
use dioxus_elements::{GlobalAttributes, SvgAttributes};
#inner
})
+2
View File
@@ -1,3 +1,5 @@
#![deny(missing_docs)]
//! Dioxus WebSys
//!
//! ## Overview