cosmic/widget/context_drawer/
overlay.rs

1// Copyright 2023 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4use crate::Element;
5
6use iced::advanced::layout::{self, Layout};
7use iced::advanced::widget::{self, Operation};
8use iced::advanced::{Clipboard, Shell};
9use iced::advanced::{overlay, renderer};
10use iced::{Event, Point, Size, mouse};
11use iced_core::{Renderer, touch};
12
13pub(super) struct Overlay<'a, 'b, Message> {
14    pub(crate) position: Point,
15    pub(super) content: &'b mut Element<'a, Message>,
16    pub(super) tree: &'b mut widget::Tree,
17    pub(super) width: f32,
18}
19
20impl<Message> overlay::Overlay<Message, crate::Theme, crate::Renderer> for Overlay<'_, '_, Message>
21where
22    Message: Clone,
23{
24    fn layout(&mut self, renderer: &crate::Renderer, bounds: Size) -> layout::Node {
25        let position = self.position;
26        let limits = layout::Limits::new(Size::ZERO, bounds)
27            .width(self.width)
28            .height(bounds.height - 8.0 - position.y);
29
30        let node = self
31            .content
32            .as_widget_mut()
33            .layout(self.tree, renderer, &limits);
34        let node_size = node.size();
35
36        node.move_to(Point {
37            x: if bounds.width > node_size.width - 8.0 {
38                bounds.width - node_size.width - 8.0
39            } else {
40                0.0
41            },
42            y: if bounds.height > node_size.height - 8.0 {
43                bounds.height - node_size.height - 8.0
44            } else {
45                0.0
46            },
47        })
48    }
49
50    fn update(
51        &mut self,
52        event: &Event,
53        layout: Layout<'_>,
54        cursor: mouse::Cursor,
55        renderer: &crate::Renderer,
56        clipboard: &mut dyn Clipboard,
57        shell: &mut Shell<'_, Message>,
58    ) {
59        self.content.as_widget_mut().update(
60            self.tree,
61            event,
62            layout,
63            cursor,
64            renderer,
65            clipboard,
66            shell,
67            &layout.bounds(),
68        );
69        match event {
70            Event::Mouse(e) if !matches!(e, mouse::Event::CursorLeft) => {
71                if cursor.is_over(layout.bounds()) {
72                    shell.capture_event();
73                }
74            }
75            Event::Touch(e) if !matches!(e, touch::Event::FingerLost { .. }) => {
76                if cursor.is_over(layout.bounds()) {
77                    shell.capture_event();
78                }
79            }
80            _ => {}
81        }
82    }
83
84    fn draw(
85        &self,
86        renderer: &mut crate::Renderer,
87        theme: &crate::Theme,
88        style: &renderer::Style,
89        layout: Layout<'_>,
90        cursor: mouse::Cursor,
91    ) {
92        renderer.with_layer(layout.bounds(), |renderer| {
93            self.content.as_widget().draw(
94                self.tree,
95                renderer,
96                theme,
97                style,
98                layout,
99                cursor,
100                &layout.bounds(),
101            );
102        });
103    }
104
105    fn operate(
106        &mut self,
107        layout: Layout<'_>,
108        renderer: &crate::Renderer,
109        operation: &mut dyn Operation<()>,
110    ) {
111        self.content
112            .as_widget_mut()
113            .operate(self.tree, layout, renderer, operation);
114    }
115
116    fn mouse_interaction(
117        &self,
118        layout: Layout<'_>,
119        cursor: mouse::Cursor,
120        renderer: &crate::Renderer,
121    ) -> mouse::Interaction {
122        // TODO how to handle viewport here?
123        let viewport = &layout.bounds();
124        let interaction = self
125            .content
126            .as_widget()
127            .mouse_interaction(self.tree, layout, cursor, viewport, renderer);
128        if let mouse::Interaction::None = interaction
129            && cursor.is_over(layout.bounds())
130        {
131            return mouse::Interaction::Idle;
132        }
133        interaction
134    }
135
136    fn overlay<'c>(
137        &'c mut self,
138        layout: Layout<'c>,
139        renderer: &crate::Renderer,
140    ) -> Option<overlay::Element<'c, Message, crate::Theme, crate::Renderer>> {
141        let viewport = &layout.bounds();
142
143        self.content.as_widget_mut().overlay(
144            self.tree,
145            layout,
146            renderer,
147            viewport,
148            iced::Vector::default(),
149        )
150    }
151}