Backed out 5 changesets (bug 1866429, bug 1866421, bug 1865046, bug 1866462, bug...
[gecko.git] / gfx / wr / examples / scrolling.rs
blob315b945d20f1de2df1b519ea14cdf11e3d63f0f3
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 extern crate euclid;
6 extern crate gleam;
7 extern crate glutin;
8 extern crate webrender;
9 extern crate winit;
11 #[path = "common/boilerplate.rs"]
12 mod boilerplate;
14 use crate::boilerplate::{Example, HandyDandyRectBuilder};
15 use euclid::SideOffsets2D;
16 use webrender::api::*;
17 use webrender::render_api::*;
18 use webrender::api::units::*;
19 use winit::dpi::LogicalPosition;
22 const EXT_SCROLL_ID_ROOT: u64 = 1;
23 const EXT_SCROLL_ID_CONTENT: u64 = 2;
25 struct App {
26     cursor_position: WorldPoint,
27     scroll_offset: LayoutVector2D,
30 impl Example for App {
31     fn render(
32         &mut self,
33         _api: &mut RenderApi,
34         builder: &mut DisplayListBuilder,
35         _txn: &mut Transaction,
36         _device_size: DeviceIntSize,
37         pipeline_id: PipelineId,
38         _document_id: DocumentId,
39     ) {
40         let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
41         builder.push_simple_stacking_context(
42             LayoutPoint::zero(),
43             root_space_and_clip.spatial_id,
44             PrimitiveFlags::IS_BACKFACE_VISIBLE,
45         );
47         if true {
48             // scrolling and clips stuff
49             // let's make a scrollbox
50             let scrollbox = (0, 0).to(300, 400);
51             builder.push_simple_stacking_context(
52                 LayoutPoint::new(10., 10.),
53                 root_space_and_clip.spatial_id,
54                 PrimitiveFlags::IS_BACKFACE_VISIBLE,
55             );
56             // set the scrolling clip
57             let space1 = builder.define_scroll_frame(
58                 root_space_and_clip.spatial_id,
59                 ExternalScrollId(EXT_SCROLL_ID_ROOT, PipelineId::dummy()),
60                 (0, 0).by(1000, 1000),
61                 scrollbox,
62                 LayoutVector2D::zero(),
63                 APZScrollGeneration::default(),
64                 HasScrollLinkedEffect::No,
65                 SpatialTreeItemKey::new(0, 0),
66             );
67             let space_and_clip1 = SpaceAndClipInfo {
68                 spatial_id: space1,
69                 clip_chain_id: root_space_and_clip.clip_chain_id,
70             };
72             // now put some content into it.
73             // start with a white background
74             let info = CommonItemProperties::new((0, 0).to(1000, 1000), space_and_clip1);
75             builder.push_hit_test(
76                 info.clip_rect,
77                 ClipChainId::INVALID,
78                 info.spatial_id,
79                 info.flags,
80                 (0, 1)
81             );
82             builder.push_rect(&info, info.clip_rect, ColorF::new(1.0, 1.0, 1.0, 1.0));
84             // let's make a 50x50 blue square as a visual reference
85             let info = CommonItemProperties::new((0, 0).to(50, 50), space_and_clip1);
86             builder.push_hit_test(
87                 info.clip_rect,
88                 ClipChainId::INVALID,
89                 info.spatial_id,
90                 info.flags,
91                 (0, 2)
92             );
93             builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 0.0, 1.0, 1.0));
95             // and a 50x50 green square next to it with an offset clip
96             // to see what that looks like
97             let info = CommonItemProperties::new(
98                 (50, 0).to(100, 50).intersection(&(60, 10).to(110, 60)).unwrap(),
99                 space_and_clip1,
100             );
101             builder.push_hit_test(
102                 info.clip_rect,
103                 ClipChainId::INVALID,
104                 info.spatial_id,
105                 info.flags,
106                 (0, 3)
107             );
108             builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 0.0, 1.0));
110             // Below the above rectangles, set up a nested scrollbox. It's still in
111             // the same stacking context, so note that the rects passed in need to
112             // be relative to the stacking context.
113             let space2 = builder.define_scroll_frame(
114                 space1,
115                 ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()),
116                 (0, 100).to(300, 1000),
117                 (0, 100).to(200, 300),
118                 LayoutVector2D::zero(),
119                 APZScrollGeneration::default(),
120                 HasScrollLinkedEffect::No,
121                 SpatialTreeItemKey::new(0, 1),
122             );
123             let space_and_clip2 = SpaceAndClipInfo {
124                 spatial_id: space2,
125                 clip_chain_id: root_space_and_clip.clip_chain_id,
126             };
128             // give it a giant gray background just to distinguish it and to easily
129             // visually identify the nested scrollbox
130             let info = CommonItemProperties::new(
131                 (-1000, -1000).to(5000, 5000),
132                 space_and_clip2,
133             );
134             builder.push_hit_test(
135                 info.clip_rect,
136                 ClipChainId::INVALID,
137                 info.spatial_id,
138                 info.flags,
139                 (0, 4)
140             );
141             builder.push_rect(&info, info.clip_rect, ColorF::new(0.5, 0.5, 0.5, 1.0));
143             // add a teal square to visualize the scrolling/clipping behaviour
144             // as you scroll the nested scrollbox
145             let info = CommonItemProperties::new((0, 200).to(50, 250), space_and_clip2);
146             builder.push_hit_test(
147                 info.clip_rect,
148                 ClipChainId::INVALID,
149                 info.spatial_id,
150                 info.flags,
151                 (0, 5)
152             );
153             builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0));
155             // Add a sticky frame. It will "stick" twice while scrolling, once
156             // at a margin of 10px from the bottom, for 40 pixels of scrolling,
157             // and once at a margin of 10px from the top, for 60 pixels of
158             // scrolling.
159             let sticky_id = builder.define_sticky_frame(
160                 space_and_clip2.spatial_id,
161                 (50, 350).by(50, 50),
162                 SideOffsets2D::new(Some(10.0), None, Some(10.0), None),
163                 StickyOffsetBounds::new(-40.0, 60.0),
164                 StickyOffsetBounds::new(0.0, 0.0),
165                 LayoutVector2D::new(0.0, 0.0),
166                 SpatialTreeItemKey::new(0, 2),
167             );
169             let info = CommonItemProperties::new(
170                 (50, 350).by(50, 50),
171                 SpaceAndClipInfo {
172                     spatial_id: sticky_id,
173                     clip_chain_id: space_and_clip2.clip_chain_id,
174                 },
175             );
176             builder.push_hit_test(
177                 info.clip_rect,
178                 ClipChainId::INVALID,
179                 info.spatial_id,
180                 info.flags,
181                 (0, 6)
182             );
183             builder.push_rect(
184                 &info,
185                 info.clip_rect,
186                 ColorF::new(0.5, 0.5, 1.0, 1.0),
187             );
189             // just for good measure add another teal square further down and to
190             // the right, which can be scrolled into view by the user
191             let info = CommonItemProperties::new(
192                 (250, 350).to(300, 400),
193                 space_and_clip2,
194             );
195             builder.push_hit_test(
196                 info.clip_rect,
197                 ClipChainId::INVALID,
198                 info.spatial_id,
199                 info.flags,
200                 (0, 7)
201             );
202             builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0));
204             builder.pop_stacking_context();
205         }
207         builder.pop_stacking_context();
208     }
210     fn on_event(
211         &mut self,
212         event: winit::event::WindowEvent,
213         window: &winit::window::Window,
214         api: &mut RenderApi,
215         document_id: DocumentId,
216     ) -> bool {
217         let mut txn = Transaction::new();
218         match event {
219             winit::event::WindowEvent::KeyboardInput {
220                 input: winit::event::KeyboardInput {
221                     state: winit::event::ElementState::Pressed,
222                     virtual_keycode: Some(key),
223                     ..
224                 },
225                 ..
226             } => {
227                 let offset = match key {
228                     winit::event::VirtualKeyCode::Down => Some(LayoutVector2D::new(0.0, -10.0)),
229                     winit::event::VirtualKeyCode::Up => Some(LayoutVector2D::new(0.0, 10.0)),
230                     winit::event::VirtualKeyCode::Right => Some(LayoutVector2D::new(-10.0, 0.0)),
231                     winit::event::VirtualKeyCode::Left => Some(LayoutVector2D::new(10.0, 0.0)),
232                     _ => None,
233                 };
235                 if let Some(offset) = offset {
236                     self.scroll_offset += offset;
238                     txn.set_scroll_offsets(
239                         ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()),
240                         vec![SampledScrollOffset {
241                             offset: self.scroll_offset,
242                             generation: APZScrollGeneration::default(),
243                         }],
244                     );
245                     txn.generate_frame(0, RenderReasons::empty());
246                 }
247             }
248             winit::event::WindowEvent::CursorMoved { position, .. } => {
249                 let pos: LogicalPosition<f32> = position.to_logical(window.scale_factor());
250                 self.cursor_position = WorldPoint::new(pos.x, pos.y);
251             }
252             winit::event::WindowEvent::MouseWheel { delta, .. } => {
253                 const LINE_HEIGHT: f32 = 38.0;
254                 let (dx, dy) = match delta {
255                     winit::event::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
256                     winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32),
257                 };
259                 self.scroll_offset += LayoutVector2D::new(dx, dy);
261                 txn.set_scroll_offsets(
262                     ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()),
263                     vec![SampledScrollOffset {
264                             offset: self.scroll_offset,
265                             generation: APZScrollGeneration::default(),
266                     }],
267                 );
269                 txn.generate_frame(0, RenderReasons::empty());
270             }
271             winit::event::WindowEvent::MouseInput { .. } => {
272                 let results = api.hit_test(
273                     document_id,
274                     self.cursor_position,
275                 );
277                 println!("Hit test results:");
278                 for item in &results.items {
279                     println!("  • {:?}", item);
280                 }
281                 println!("");
282             }
283             _ => (),
284         }
286         api.send_transaction(document_id, txn);
288         false
289     }
292 fn main() {
293     let mut app = App {
294         cursor_position: WorldPoint::zero(),
295         scroll_offset: LayoutVector2D::zero(),
296     };
297     boilerplate::main_wrapper(&mut app, None);