diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/wr/examples/scrolling.rs | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/gfx/wr/examples/scrolling.rs b/gfx/wr/examples/scrolling.rs new file mode 100644 index 0000000000..034af95244 --- /dev/null +++ b/gfx/wr/examples/scrolling.rs @@ -0,0 +1,243 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +extern crate euclid; +extern crate gleam; +extern crate glutin; +extern crate webrender; +extern crate winit; + +#[path = "common/boilerplate.rs"] +mod boilerplate; + +use crate::boilerplate::{Example, HandyDandyRectBuilder}; +use euclid::SideOffsets2D; +use webrender::api::*; +use webrender::render_api::*; +use webrender::api::units::*; +use winit::dpi::LogicalPosition; + + +const EXT_SCROLL_ID_ROOT: u64 = 1; +const EXT_SCROLL_ID_CONTENT: u64 = 2; + +struct App { + cursor_position: WorldPoint, + scroll_origin: LayoutPoint, +} + +impl Example for App { + fn render( + &mut self, + _api: &mut RenderApi, + builder: &mut DisplayListBuilder, + _txn: &mut Transaction, + _device_size: DeviceIntSize, + pipeline_id: PipelineId, + _document_id: DocumentId, + ) { + let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id); + builder.push_simple_stacking_context( + LayoutPoint::zero(), + root_space_and_clip.spatial_id, + PrimitiveFlags::IS_BACKFACE_VISIBLE, + ); + + if true { + // scrolling and clips stuff + // let's make a scrollbox + let scrollbox = (0, 0).to(300, 400); + builder.push_simple_stacking_context( + LayoutPoint::new(10., 10.), + root_space_and_clip.spatial_id, + PrimitiveFlags::IS_BACKFACE_VISIBLE, + ); + // set the scrolling clip + let space_and_clip1 = builder.define_scroll_frame( + &root_space_and_clip, + ExternalScrollId(EXT_SCROLL_ID_ROOT, PipelineId::dummy()), + (0, 0).by(1000, 1000), + scrollbox, + ScrollSensitivity::ScriptAndInputEvents, + LayoutVector2D::zero(), + ); + + // now put some content into it. + // start with a white background + let info = CommonItemProperties::new((0, 0).to(1000, 1000), space_and_clip1); + builder.push_hit_test(&info, (0, 1)); + builder.push_rect(&info, info.clip_rect, ColorF::new(1.0, 1.0, 1.0, 1.0)); + + // let's make a 50x50 blue square as a visual reference + let info = CommonItemProperties::new((0, 0).to(50, 50), space_and_clip1); + builder.push_hit_test(&info, (0, 2)); + builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 0.0, 1.0, 1.0)); + + // and a 50x50 green square next to it with an offset clip + // to see what that looks like + let info = CommonItemProperties::new( + (50, 0).to(100, 50).intersection(&(60, 10).to(110, 60)).unwrap(), + space_and_clip1, + ); + builder.push_hit_test(&info, (0, 3)); + builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 0.0, 1.0)); + + // Below the above rectangles, set up a nested scrollbox. It's still in + // the same stacking context, so note that the rects passed in need to + // be relative to the stacking context. + let space_and_clip2 = builder.define_scroll_frame( + &space_and_clip1, + ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), + (0, 100).to(300, 1000), + (0, 100).to(200, 300), + ScrollSensitivity::ScriptAndInputEvents, + LayoutVector2D::zero(), + ); + + // give it a giant gray background just to distinguish it and to easily + // visually identify the nested scrollbox + let info = CommonItemProperties::new( + (-1000, -1000).to(5000, 5000), + space_and_clip2, + ); + builder.push_hit_test(&info, (0, 4)); + builder.push_rect(&info, info.clip_rect, ColorF::new(0.5, 0.5, 0.5, 1.0)); + + // add a teal square to visualize the scrolling/clipping behaviour + // as you scroll the nested scrollbox + let info = CommonItemProperties::new((0, 200).to(50, 250), space_and_clip2); + builder.push_hit_test(&info, (0, 5)); + builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0)); + + // Add a sticky frame. It will "stick" twice while scrolling, once + // at a margin of 10px from the bottom, for 40 pixels of scrolling, + // and once at a margin of 10px from the top, for 60 pixels of + // scrolling. + let sticky_id = builder.define_sticky_frame( + space_and_clip2.spatial_id, + (50, 350).by(50, 50), + SideOffsets2D::new(Some(10.0), None, Some(10.0), None), + StickyOffsetBounds::new(-40.0, 60.0), + StickyOffsetBounds::new(0.0, 0.0), + LayoutVector2D::new(0.0, 0.0) + ); + + let info = CommonItemProperties::new( + (50, 350).by(50, 50), + SpaceAndClipInfo { + spatial_id: sticky_id, + clip_id: space_and_clip2.clip_id, + }, + ); + builder.push_hit_test(&info, (0, 6)); + builder.push_rect( + &info, + info.clip_rect, + ColorF::new(0.5, 0.5, 1.0, 1.0), + ); + + // just for good measure add another teal square further down and to + // the right, which can be scrolled into view by the user + let info = CommonItemProperties::new( + (250, 350).to(300, 400), + space_and_clip2, + ); + builder.push_hit_test(&info, (0, 7)); + builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0)); + + builder.pop_stacking_context(); + } + + builder.pop_stacking_context(); + } + + fn on_event(&mut self, event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool { + let mut txn = Transaction::new(); + match event { + winit::WindowEvent::KeyboardInput { + input: winit::KeyboardInput { + state: winit::ElementState::Pressed, + virtual_keycode: Some(key), + .. + }, + .. + } => { + let offset = match key { + winit::VirtualKeyCode::Down => Some(LayoutVector2D::new(0.0, -10.0)), + winit::VirtualKeyCode::Up => Some(LayoutVector2D::new(0.0, 10.0)), + winit::VirtualKeyCode::Right => Some(LayoutVector2D::new(-10.0, 0.0)), + winit::VirtualKeyCode::Left => Some(LayoutVector2D::new(10.0, 0.0)), + _ => None, + }; + let zoom = match key { + winit::VirtualKeyCode::Key0 => Some(1.0), + winit::VirtualKeyCode::Minus => Some(0.8), + winit::VirtualKeyCode::Equals => Some(1.25), + _ => None, + }; + + if let Some(offset) = offset { + self.scroll_origin += offset; + + txn.scroll_node_with_id( + self.scroll_origin, + ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), + ScrollClamping::ToContentBounds, + ); + txn.generate_frame(0); + } + if let Some(zoom) = zoom { + txn.set_pinch_zoom(ZoomFactor::new(zoom)); + txn.generate_frame(0); + } + } + winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => { + self.cursor_position = WorldPoint::new(x as f32, y as f32); + } + winit::WindowEvent::MouseWheel { delta, .. } => { + const LINE_HEIGHT: f32 = 38.0; + let (dx, dy) = match delta { + winit::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT), + winit::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32), + }; + + self.scroll_origin += LayoutVector2D::new(dx, dy); + + txn.scroll_node_with_id( + self.scroll_origin, + ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), + ScrollClamping::ToContentBounds, + ); + + txn.generate_frame(0); + } + winit::WindowEvent::MouseInput { .. } => { + let results = api.hit_test( + document_id, + None, + self.cursor_position, + ); + + println!("Hit test results:"); + for item in &results.items { + println!(" • {:?}", item); + } + println!(""); + } + _ => (), + } + + api.send_transaction(document_id, txn); + + false + } +} + +fn main() { + let mut app = App { + cursor_position: WorldPoint::zero(), + scroll_origin: LayoutPoint::zero(), + }; + boilerplate::main_wrapper(&mut app, None); +} |