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/. */
4
5 extern crate euclid;
6 extern crate gleam;
7 extern crate glutin;
8 extern crate webrender;
9
10 #[path = "common/boilerplate.rs"]
11 mod boilerplate;
12
13 use boilerplate::{Example, HandyDandyRectBuilder};
14 use euclid::SideOffsets2D;
15 use webrender::api::*;
16
17 struct App {
18 cursor_position: WorldPoint,
19 }
20
21 impl Example for App {
render( &mut self, _api: &RenderApi, builder: &mut DisplayListBuilder, _resources: &mut ResourceUpdates, _framebuffer_size: DeviceUintSize, _pipeline_id: PipelineId, _document_id: DocumentId, )22 fn render(
23 &mut self,
24 _api: &RenderApi,
25 builder: &mut DisplayListBuilder,
26 _resources: &mut ResourceUpdates,
27 _framebuffer_size: DeviceUintSize,
28 _pipeline_id: PipelineId,
29 _document_id: DocumentId,
30 ) {
31 let info = LayoutPrimitiveInfo::new(
32 LayoutRect::new(LayoutPoint::zero(), builder.content_size())
33 );
34 builder.push_stacking_context(
35 &info,
36 ScrollPolicy::Scrollable,
37 None,
38 TransformStyle::Flat,
39 None,
40 MixBlendMode::Normal,
41 Vec::new(),
42 );
43
44 if true {
45 // scrolling and clips stuff
46 // let's make a scrollbox
47 let scrollbox = (0, 0).to(300, 400);
48 builder.push_stacking_context(
49 &LayoutPrimitiveInfo::new((10, 10).by(0, 0)),
50 ScrollPolicy::Scrollable,
51 None,
52 TransformStyle::Flat,
53 None,
54 MixBlendMode::Normal,
55 Vec::new(),
56 );
57 // set the scrolling clip
58 let clip_id = builder.define_scroll_frame(
59 None,
60 (0, 0).by(1000, 1000),
61 scrollbox,
62 vec![],
63 None,
64 ScrollSensitivity::ScriptAndInputEvents,
65 );
66 builder.push_clip_id(clip_id);
67
68 // now put some content into it.
69 // start with a white background
70 let mut info = LayoutPrimitiveInfo::new((0, 0).to(1000, 1000));
71 info.tag = Some((0, 1));
72 builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
73
74 // let's make a 50x50 blue square as a visual reference
75 let mut info = LayoutPrimitiveInfo::new((0, 0).to(50, 50));
76 info.tag = Some((0, 2));
77 builder.push_rect(&info, ColorF::new(0.0, 0.0, 1.0, 1.0));
78
79 // and a 50x50 green square next to it with an offset clip
80 // to see what that looks like
81 let mut info =
82 LayoutPrimitiveInfo::with_clip_rect((50, 0).to(100, 50), (60, 10).to(110, 60));
83 info.tag = Some((0, 3));
84 builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
85
86 // Below the above rectangles, set up a nested scrollbox. It's still in
87 // the same stacking context, so note that the rects passed in need to
88 // be relative to the stacking context.
89 let nested_clip_id = builder.define_scroll_frame(
90 None,
91 (0, 100).to(300, 1000),
92 (0, 100).to(200, 300),
93 vec![],
94 None,
95 ScrollSensitivity::ScriptAndInputEvents,
96 );
97 builder.push_clip_id(nested_clip_id);
98
99 // give it a giant gray background just to distinguish it and to easily
100 // visually identify the nested scrollbox
101 let mut info = LayoutPrimitiveInfo::new((-1000, -1000).to(5000, 5000));
102 info.tag = Some((0, 4));
103 builder.push_rect(&info, ColorF::new(0.5, 0.5, 0.5, 1.0));
104
105 // add a teal square to visualize the scrolling/clipping behaviour
106 // as you scroll the nested scrollbox
107 let mut info = LayoutPrimitiveInfo::new((0, 200).to(50, 250));
108 info.tag = Some((0, 5));
109 builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
110
111 // Add a sticky frame. It will "stick" twice while scrolling, once
112 // at a margin of 10px from the bottom, for 40 pixels of scrolling,
113 // and once at a margin of 10px from the top, for 60 pixels of
114 // scrolling.
115 let sticky_id = builder.define_sticky_frame(
116 (50, 350).by(50, 50),
117 SideOffsets2D::new(Some(10.0), None, Some(10.0), None),
118 StickyOffsetBounds::new(-40.0, 60.0),
119 StickyOffsetBounds::new(0.0, 0.0),
120 LayoutVector2D::new(0.0, 0.0)
121 );
122
123 builder.push_clip_id(sticky_id);
124 let mut info = LayoutPrimitiveInfo::new((50, 350).by(50, 50));
125 info.tag = Some((0, 6));
126 builder.push_rect(&info, ColorF::new(0.5, 0.5, 1.0, 1.0));
127 builder.pop_clip_id(); // sticky_id
128
129 // just for good measure add another teal square further down and to
130 // the right, which can be scrolled into view by the user
131 let mut info = LayoutPrimitiveInfo::new((250, 350).to(300, 400));
132 info.tag = Some((0, 7));
133 builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
134
135 builder.pop_clip_id(); // nested_clip_id
136
137 builder.pop_clip_id(); // clip_id
138 builder.pop_stacking_context();
139 }
140
141 builder.pop_stacking_context();
142 }
143
on_event(&mut self, event: glutin::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool144 fn on_event(&mut self, event: glutin::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
145 let mut txn = Transaction::new();
146 match event {
147 glutin::WindowEvent::KeyboardInput {
148 input: glutin::KeyboardInput {
149 state: glutin::ElementState::Pressed,
150 virtual_keycode: Some(key),
151 ..
152 },
153 ..
154 } => {
155 let offset = match key {
156 glutin::VirtualKeyCode::Down => (0.0, -10.0),
157 glutin::VirtualKeyCode::Up => (0.0, 10.0),
158 glutin::VirtualKeyCode::Right => (-10.0, 0.0),
159 glutin::VirtualKeyCode::Left => (10.0, 0.0),
160 _ => return false,
161 };
162
163 txn.scroll(
164 ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
165 self.cursor_position,
166 ScrollEventPhase::Start,
167 );
168 }
169 glutin::WindowEvent::CursorMoved { position: (x, y), .. } => {
170 self.cursor_position = WorldPoint::new(x as f32, y as f32);
171 }
172 glutin::WindowEvent::MouseWheel { delta, .. } => {
173 const LINE_HEIGHT: f32 = 38.0;
174 let (dx, dy) = match delta {
175 glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
176 glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
177 };
178
179 txn.scroll(
180 ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
181 self.cursor_position,
182 ScrollEventPhase::Start,
183 );
184 }
185 glutin::WindowEvent::MouseInput { .. } => {
186 let results = api.hit_test(
187 document_id,
188 None,
189 self.cursor_position,
190 HitTestFlags::FIND_ALL
191 );
192
193 println!("Hit test results:");
194 for item in &results.items {
195 println!(" • {:?}", item);
196 }
197 println!("");
198 }
199 _ => (),
200 }
201
202 api.send_transaction(document_id, txn);
203
204 false
205 }
206 }
207
main()208 fn main() {
209 let mut app = App {
210 cursor_position: WorldPoint::zero(),
211 };
212 boilerplate::main_wrapper(&mut app, None);
213 }
214