1use crate::draw::*;
2use crate::event::{Event, Key};
3use crate::layout::{Rectangle, Size};
4use crate::node::{GenericNode, IntoNode, Node};
5use crate::style::Stylesheet;
6use crate::widget::{Context, Widget};
7
8pub struct Window<'a, T> {
10 title: Option<Node<'a, T>>,
11 content: Option<Node<'a, T>>,
12}
13
14pub struct State {
16 x: f32,
17 y: f32,
18 cursor_x: f32,
19 cursor_y: f32,
20 inner: InnerState,
21}
22
23#[derive(Clone, Copy)]
24enum InnerState {
25 Idle,
26 Dragging(f32, f32),
27}
28
29impl<'a, T: 'a> Window<'a, T> {
30 pub fn new(title: impl IntoNode<'a, T>, content: impl IntoNode<'a, T>) -> Self {
32 Self {
33 title: Some(title.into_node()),
34 content: Some(content.into_node()),
35 }
36 }
37
38 pub fn extend<I: IntoIterator<Item = N>, N: IntoNode<'a, T>>(mut self, iter: I) -> Self {
41 let mut iter = iter.into_iter();
42 if self.title.is_none() {
43 self.title = iter.next().map(IntoNode::into_node);
44 }
45 if self.content.is_none() {
46 self.content = iter.next().map(IntoNode::into_node);
47 }
48 self
49 }
50
51 fn layout(&self, state: &State, viewport: Rectangle, style: &Stylesheet) -> (Rectangle, Rectangle, Rectangle) {
52 let title_size = self.title().size();
53 let title_width = title_size.0.min_size();
54 let title_height = title_size.1.min_size();
55 let content_size = self.content().size();
56 let content_width = content_size.0.min_size();
57 let content_height = content_size.1.min_size();
58 let width = title_width.max(content_width);
59 let height = title_height + content_height;
60 let padding = style.background.padding();
61 let padding = Rectangle {
62 left: padding.left + style.padding.left,
63 right: padding.right + style.padding.right,
64 top: padding.top + style.padding.top,
65 bottom: padding.bottom + style.padding.bottom,
66 };
67 let layout = Rectangle::from_xywh(
68 viewport.left + state.x,
69 viewport.top + state.y,
70 width + padding.left + padding.right,
71 height + padding.top + padding.bottom,
72 );
73 let title_content = layout.after_padding(padding);
74 let title = Rectangle::from_xywh(
75 title_content.left,
76 title_content.top,
77 title_size.0.resolve(title_content.width(), title_size.0.parts()),
78 title_height,
79 );
80 let content = Rectangle::from_xywh(
81 title_content.left,
82 title_content.top + title_height,
83 content_size.0.resolve(title_content.width(), content_size.0.parts()),
84 content_height,
85 );
86 let align = |rect: Rectangle| {
87 rect.translate(
88 style
89 .align_horizontal
90 .resolve_start(rect.width(), title_content.width()),
91 0.0,
92 )
93 };
94 (layout, align(title), align(content))
95 }
96
97 fn content(&self) -> &Node<'a, T> {
98 self.content.as_ref().expect("content of `Window` must be set")
99 }
100
101 fn content_mut(&mut self) -> &mut Node<'a, T> {
102 self.content.as_mut().expect("content of `Window` must be set")
103 }
104
105 fn title(&self) -> &Node<'a, T> {
106 self.title.as_ref().expect("title of `Window` must be set")
107 }
108
109 fn title_mut(&mut self) -> &mut Node<'a, T> {
110 self.title.as_mut().expect("title of `Window` must be set")
111 }
112}
113
114impl<'a, T: 'a> Default for Window<'a, T> {
115 fn default() -> Self {
116 Self {
117 title: None,
118 content: None,
119 }
120 }
121}
122
123impl<'a, T: 'a> Widget<'a, T> for Window<'a, T> {
124 type State = State;
125
126 fn mount(&self) -> Self::State {
127 State::default()
128 }
129
130 fn widget(&self) -> &'static str {
131 "window"
132 }
133
134 fn len(&self) -> usize {
135 2
136 }
137
138 fn visit_children(&mut self, visitor: &mut dyn FnMut(&mut dyn GenericNode<'a, T>)) {
139 visitor(&mut **self.title_mut());
140 visitor(&mut **self.content_mut());
141 }
142
143 fn size(&self, _: &State, _: &Stylesheet) -> (Size, Size) {
144 (Size::Fill(1), Size::Fill(1))
145 }
146
147 fn hit(&self, state: &State, viewport: Rectangle, clip: Rectangle, style: &Stylesheet, x: f32, y: f32) -> bool {
148 if clip.point_inside(x, y) {
149 let (layout, _, _) = self.layout(state, viewport, style);
150 layout.point_inside(x, y)
151 } else {
152 false
153 }
154 }
155
156 fn focused(&self, _: &State) -> bool {
157 self.title().focused() || self.content().focused()
158 }
159
160 fn event(
161 &mut self,
162 state: &mut State,
163 viewport: Rectangle,
164 clip: Rectangle,
165 style: &Stylesheet,
166 event: Event,
167 context: &mut Context<T>,
168 ) {
169 let (layout, title, content) = self.layout(&*state, viewport, style);
170
171 if self.title().focused() {
172 self.title_mut().event(title, clip, event, context);
173 return;
174 }
175
176 if self.content().focused() {
177 self.content_mut().event(content, clip, event, context);
178 return;
179 }
180
181 match (event, state.inner) {
182 (Event::Cursor(x, y), InnerState::Idle) => {
183 state.cursor_x = x;
184 state.cursor_y = y;
185 }
186
187 (Event::Press(Key::LeftMouseButton), InnerState::Idle) => {
188 if clip.point_inside(state.cursor_x, state.cursor_y)
189 && title.point_inside(state.cursor_x, state.cursor_y)
190 {
191 context.redraw();
192 state.inner = InnerState::Dragging(state.cursor_x - layout.left, state.cursor_y - layout.top);
193 }
194 }
195
196 (Event::Cursor(x, y), InnerState::Dragging(anchor_x, anchor_y)) => {
197 context.redraw();
198 state.cursor_x = x;
199 state.cursor_y = y;
200 state.x = (x - anchor_x).max(0.0).min(viewport.width() - layout.width());
201 state.y = (y - anchor_y).max(0.0).min(viewport.height() - layout.height());
202 }
203
204 (Event::Release(Key::LeftMouseButton), InnerState::Dragging(_, _)) => {
205 state.inner = InnerState::Idle;
206 }
207
208 _ => (),
209 }
210
211 self.title_mut().event(title, clip, event, context);
212 self.content_mut().event(content, clip, event, context);
213 }
214
215 fn draw(
216 &mut self,
217 state: &mut State,
218 viewport: Rectangle,
219 clip: Rectangle,
220 style: &Stylesheet,
221 ) -> Vec<Primitive<'a>> {
222 let (layout, title, content) = self.layout(&*state, viewport, style);
223
224 let mut result = Vec::new();
225 result.extend(style.background.render(layout));
226 result.extend(self.title_mut().draw(title, clip));
227 result.extend(self.content_mut().draw(content, clip));
228 result
229 }
230}
231
232impl<'a, T: 'a> IntoNode<'a, T> for Window<'a, T> {
233 fn into_node(self) -> Node<'a, T> {
234 Node::from_widget(self)
235 }
236}
237
238impl Default for State {
239 fn default() -> Self {
240 Self {
241 x: 0.0,
242 y: 0.0,
243 cursor_x: 0.0,
244 cursor_y: 0.0,
245 inner: InnerState::Idle,
246 }
247 }
248}