diff options
| author | 2021-12-19 11:41:18 +0700 | |
|---|---|---|
| committer | 2021-12-19 11:41:18 +0700 | |
| commit | d19858bce8f4e425837d55c9bda44c258b2dd176 (patch) | |
| tree | 72b7ca0dbe22aa5299992d63c5152d1f1bb19bc2 /lazy | |
| parent | e24eaa7c665f6b73aa4e9bac4b723cd8d24b78e1 (diff) | |
| parent | 44c0d75953984b0b31ac723bc040c016f572244d (diff) | |
| download | iced-d19858bce8f4e425837d55c9bda44c258b2dd176.tar.gz iced-d19858bce8f4e425837d55c9bda44c258b2dd176.tar.bz2 iced-d19858bce8f4e425837d55c9bda44c258b2dd176.zip  | |
Merge pull request #1154 from nicksenger/overlay-components
Implement `overlay` for `Component`
Diffstat (limited to '')
| -rw-r--r-- | lazy/src/component.rs | 335 | 
1 files changed, 280 insertions, 55 deletions
diff --git a/lazy/src/component.rs b/lazy/src/component.rs index 698f1796..787adc10 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -4,10 +4,12 @@ use iced_native::mouse;  use iced_native::overlay;  use iced_native::renderer;  use iced_native::{ -    Clipboard, Element, Hasher, Length, Point, Rectangle, Shell, Widget, +    Clipboard, Element, Hasher, Length, Point, Rectangle, Shell, Size, Widget,  };  use ouroboros::self_referencing; +use std::cell::RefCell; +use std::hash::Hash;  use std::marker::PhantomData;  pub fn view<'a, C, Message, Renderer>( @@ -19,16 +21,22 @@ where      Renderer: iced_native::Renderer + 'a,  {      Element::new(Instance { -        state: Some( +        state: RefCell::new(Some(              StateBuilder {                  component: Box::new(component), -                cache_builder: |state| Cache { -                    element: state.view(), -                    message: PhantomData, +                cache_builder: |state| { +                    Some( +                        CacheBuilder { +                            element: state.view(), +                            message: PhantomData, +                            overlay_builder: |_| None, +                        } +                        .build(), +                    )                  },              }              .build(), -        ), +        )),      })  } @@ -41,7 +49,7 @@ pub trait Component<Message, Renderer> {  }  struct Instance<'a, Message, Renderer, Event> { -    state: Option<State<'a, Message, Renderer, Event>>, +    state: RefCell<Option<State<'a, Message, Renderer, Event>>>,  }  #[self_referencing] @@ -50,12 +58,51 @@ struct State<'a, Message: 'a, Renderer: 'a, Event: 'a> {      #[borrows(mut component)]      #[covariant] -    cache: Cache<'this, Message, Renderer, Event>, +    cache: Option<Cache<'this, Message, Renderer, Event>>,  } -struct Cache<'a, Message, Renderer, Event> { +#[self_referencing] +struct Cache<'a, Message, Renderer: 'a, Event: 'a> {      element: Element<'a, Event, Renderer>,      message: PhantomData<Message>, + +    #[borrows(mut element)] +    #[covariant] +    overlay: Option<overlay::Element<'this, Event, Renderer>>, +} + +impl<'a, Message, Renderer, Event> Instance<'a, Message, Renderer, Event> { +    fn with_element<T>( +        &self, +        f: impl FnOnce(&Element<'_, Event, Renderer>) -> T, +    ) -> T { +        self.with_element_mut(|element| f(element)) +    } + +    fn with_element_mut<T>( +        &self, +        f: impl FnOnce(&mut Element<'_, Event, Renderer>) -> T, +    ) -> T { +        self.state +            .borrow_mut() +            .as_mut() +            .unwrap() +            .with_cache_mut(|cache| { +                let mut element = cache.take().unwrap().into_heads().element; +                let result = f(&mut element); + +                *cache = Some( +                    CacheBuilder { +                        element, +                        message: PhantomData, +                        overlay_builder: |_| None, +                    } +                    .build(), +                ); + +                result +            }) +    }  }  impl<'a, Message, Renderer, Event> Widget<Message, Renderer> @@ -64,11 +111,11 @@ where      Renderer: iced_native::Renderer,  {      fn width(&self) -> Length { -        self.state.as_ref().unwrap().borrow_cache().element.width() +        self.with_element(|element| element.width())      }      fn height(&self) -> Length { -        self.state.as_ref().unwrap().borrow_cache().element.height() +        self.with_element(|element| element.height())      }      fn layout( @@ -76,12 +123,7 @@ where          renderer: &Renderer,          limits: &layout::Limits,      ) -> layout::Node { -        self.state -            .as_ref() -            .unwrap() -            .borrow_cache() -            .element -            .layout(renderer, limits) +        self.with_element(|element| element.layout(renderer, limits))      }      fn on_event( @@ -96,21 +138,25 @@ where          let mut local_messages = Vec::new();          let mut local_shell = Shell::new(&mut local_messages); -        let event_status = -            self.state.as_mut().unwrap().with_cache_mut(|cache| { -                cache.element.on_event( -                    event, -                    layout, -                    cursor_position, -                    renderer, -                    clipboard, -                    &mut local_shell, -                ) -            }); +        let event_status = self.with_element_mut(|element| { +            element.on_event( +                event, +                layout, +                cursor_position, +                renderer, +                clipboard, +                &mut local_shell, +            ) +        });          if !local_messages.is_empty() { -            let mut component = -                self.state.take().unwrap().into_heads().component; +            let mut component = self +                .state +                .borrow_mut() +                .take() +                .unwrap() +                .into_heads() +                .component;              for message in local_messages                  .into_iter() @@ -119,12 +165,18 @@ where                  shell.publish(message);              } -            self.state = Some( +            *self.state.borrow_mut() = Some(                  StateBuilder {                      component, -                    cache_builder: |state| Cache { -                        element: state.view(), -                        message: PhantomData, +                    cache_builder: |state| { +                        Some( +                            CacheBuilder { +                                element: state.view(), +                                message: PhantomData, +                                overlay_builder: |_| None, +                            } +                            .build(), +                        )                      },                  }                  .build(), @@ -144,22 +196,15 @@ where          cursor_position: Point,          viewport: &Rectangle,      ) { -        self.state.as_ref().unwrap().borrow_cache().element.draw( -            renderer, -            style, -            layout, -            cursor_position, -            viewport, -        ) +        self.with_element(|element| { +            element.draw(renderer, style, layout, cursor_position, viewport); +        });      }      fn hash_layout(&self, state: &mut Hasher) { -        self.state -            .as_ref() -            .unwrap() -            .borrow_cache() -            .element -            .hash_layout(state) +        self.with_element(|element| { +            element.hash_layout(state); +        });      }      fn mouse_interaction( @@ -168,19 +213,199 @@ where          cursor_position: Point,          viewport: &Rectangle,      ) -> mouse::Interaction { -        self.state +        self.with_element(|element| { +            element.mouse_interaction(layout, cursor_position, viewport) +        }) +    } + +    fn overlay( +        &mut self, +        layout: Layout<'_>, +    ) -> Option<overlay::Element<'_, Message, Renderer>> { +        let has_overlay = self +            .state +            .borrow_mut() +            .as_mut() +            .unwrap() +            .with_cache_mut(|cache| { +                let element = cache.take().unwrap().into_heads().element; + +                *cache = Some( +                    CacheBuilder { +                        element, +                        message: PhantomData, +                        overlay_builder: |element| element.overlay(layout), +                    } +                    .build(), +                ); + +                cache.as_ref().unwrap().borrow_overlay().is_some() +            }); + +        has_overlay.then(|| { +            overlay::Element::new( +                layout.position(), +                Box::new(Overlay { instance: self }), +            ) +        }) +    } +} + +struct Overlay<'a, 'b, Message, Renderer, Event> { +    instance: &'b mut Instance<'a, Message, Renderer, Event>, +} + +impl<'a, 'b, Message, Renderer, Event> +    Overlay<'a, 'b, Message, Renderer, Event> +{ +    fn with_overlay_maybe<T>( +        &self, +        f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T, +    ) -> Option<T> { +        self.instance +            .state +            .borrow()              .as_ref()              .unwrap()              .borrow_cache() -            .element -            .mouse_interaction(layout, cursor_position, viewport) +            .as_ref() +            .unwrap() +            .borrow_overlay() +            .as_ref() +            .map(f)      } -    fn overlay( +    fn with_overlay_mut_maybe<T>( +        &self, +        f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T, +    ) -> Option<T> { +        self.instance +            .state +            .borrow_mut() +            .as_mut() +            .unwrap() +            .with_cache_mut(|cache| { +                cache +                    .as_mut() +                    .unwrap() +                    .with_overlay_mut(|overlay| overlay.as_mut().map(f)) +            }) +    } +} + +impl<'a, 'b, Message, Renderer, Event> overlay::Overlay<Message, Renderer> +    for Overlay<'a, 'b, Message, Renderer, Event> +where +    Renderer: iced_native::Renderer, +{ +    fn layout( +        &self, +        renderer: &Renderer, +        bounds: Size, +        position: Point, +    ) -> layout::Node { +        self.with_overlay_maybe(|overlay| { +            let vector = position - overlay.position(); + +            overlay.layout(renderer, bounds).translate(vector) +        }) +        .unwrap_or_default() +    } + +    fn draw( +        &self, +        renderer: &mut Renderer, +        style: &renderer::Style, +        layout: Layout<'_>, +        cursor_position: Point, +    ) { +        self.with_overlay_maybe(|overlay| { +            overlay.draw(renderer, style, layout, cursor_position); +        }); +    } + +    fn mouse_interaction( +        &self, +        layout: Layout<'_>, +        cursor_position: Point, +        viewport: &Rectangle, +    ) -> mouse::Interaction { +        self.with_overlay_maybe(|overlay| { +            overlay.mouse_interaction(layout, cursor_position, viewport) +        }) +        .unwrap_or_default() +    } + +    fn hash_layout(&self, state: &mut Hasher, position: Point) { +        struct Marker; +        std::any::TypeId::of::<Marker>().hash(state); + +        (position.x as u32).hash(state); +        (position.y as u32).hash(state); + +        self.with_overlay_maybe(|overlay| { +            overlay.hash_layout(state); +        }); +    } + +    fn on_event(          &mut self, -        _layout: Layout<'_>, -    ) -> Option<overlay::Element<'_, Message, Renderer>> { -        // TODO: Rethink overlay composability -        None +        event: iced_native::Event, +        layout: Layout<'_>, +        cursor_position: Point, +        renderer: &Renderer, +        clipboard: &mut dyn Clipboard, +        shell: &mut Shell<'_, Message>, +    ) -> iced_native::event::Status { +        let mut local_messages = Vec::new(); +        let mut local_shell = Shell::new(&mut local_messages); + +        let event_status = self +            .with_overlay_mut_maybe(|overlay| { +                overlay.on_event( +                    event, +                    layout, +                    cursor_position, +                    renderer, +                    clipboard, +                    &mut local_shell, +                ) +            }) +            .unwrap_or_else(|| iced_native::event::Status::Ignored); + +        if !local_messages.is_empty() { +            let mut component = +                self.instance.state.take().unwrap().into_heads().component; + +            for message in local_messages +                .into_iter() +                .filter_map(|message| component.update(message)) +            { +                shell.publish(message); +            } + +            self.instance.state = RefCell::new(Some( +                StateBuilder { +                    component, +                    cache_builder: |state| { +                        Some( +                            CacheBuilder { +                                element: state.view(), +                                message: PhantomData, +                                overlay_builder: |element| { +                                    element.overlay(layout) +                                }, +                            } +                            .build(), +                        ) +                    }, +                } +                .build(), +            )); + +            shell.invalidate_layout(); +        } + +        event_status      }  }  | 
