diff options
Diffstat (limited to 'gfx/wr/examples/scrolling.rs')
-rw-r--r-- | gfx/wr/examples/scrolling.rs | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/gfx/wr/examples/scrolling.rs b/gfx/wr/examples/scrolling.rs new file mode 100644 index 0000000000..315b945d20 --- /dev/null +++ b/gfx/wr/examples/scrolling.rs @@ -0,0 +1,298 @@ +/* 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_offset: LayoutVector2D, +} + +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 space1 = builder.define_scroll_frame( + root_space_and_clip.spatial_id, + ExternalScrollId(EXT_SCROLL_ID_ROOT, PipelineId::dummy()), + (0, 0).by(1000, 1000), + scrollbox, + LayoutVector2D::zero(), + APZScrollGeneration::default(), + HasScrollLinkedEffect::No, + SpatialTreeItemKey::new(0, 0), + ); + let space_and_clip1 = SpaceAndClipInfo { + spatial_id: space1, + clip_chain_id: root_space_and_clip.clip_chain_id, + }; + + // 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.clip_rect, + ClipChainId::INVALID, + info.spatial_id, + info.flags, + (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.clip_rect, + ClipChainId::INVALID, + info.spatial_id, + info.flags, + (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.clip_rect, + ClipChainId::INVALID, + info.spatial_id, + info.flags, + (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 space2 = builder.define_scroll_frame( + space1, + ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), + (0, 100).to(300, 1000), + (0, 100).to(200, 300), + LayoutVector2D::zero(), + APZScrollGeneration::default(), + HasScrollLinkedEffect::No, + SpatialTreeItemKey::new(0, 1), + ); + let space_and_clip2 = SpaceAndClipInfo { + spatial_id: space2, + clip_chain_id: root_space_and_clip.clip_chain_id, + }; + + // 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.clip_rect, + ClipChainId::INVALID, + info.spatial_id, + info.flags, + (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.clip_rect, + ClipChainId::INVALID, + info.spatial_id, + info.flags, + (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), + SpatialTreeItemKey::new(0, 2), + ); + + let info = CommonItemProperties::new( + (50, 350).by(50, 50), + SpaceAndClipInfo { + spatial_id: sticky_id, + clip_chain_id: space_and_clip2.clip_chain_id, + }, + ); + builder.push_hit_test( + info.clip_rect, + ClipChainId::INVALID, + info.spatial_id, + info.flags, + (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.clip_rect, + ClipChainId::INVALID, + info.spatial_id, + info.flags, + (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::event::WindowEvent, + window: &winit::window::Window, + api: &mut RenderApi, + document_id: DocumentId, + ) -> bool { + let mut txn = Transaction::new(); + match event { + winit::event::WindowEvent::KeyboardInput { + input: winit::event::KeyboardInput { + state: winit::event::ElementState::Pressed, + virtual_keycode: Some(key), + .. + }, + .. + } => { + let offset = match key { + winit::event::VirtualKeyCode::Down => Some(LayoutVector2D::new(0.0, -10.0)), + winit::event::VirtualKeyCode::Up => Some(LayoutVector2D::new(0.0, 10.0)), + winit::event::VirtualKeyCode::Right => Some(LayoutVector2D::new(-10.0, 0.0)), + winit::event::VirtualKeyCode::Left => Some(LayoutVector2D::new(10.0, 0.0)), + _ => None, + }; + + if let Some(offset) = offset { + self.scroll_offset += offset; + + txn.set_scroll_offsets( + ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), + vec![SampledScrollOffset { + offset: self.scroll_offset, + generation: APZScrollGeneration::default(), + }], + ); + txn.generate_frame(0, RenderReasons::empty()); + } + } + winit::event::WindowEvent::CursorMoved { position, .. } => { + let pos: LogicalPosition<f32> = position.to_logical(window.scale_factor()); + self.cursor_position = WorldPoint::new(pos.x, pos.y); + } + winit::event::WindowEvent::MouseWheel { delta, .. } => { + const LINE_HEIGHT: f32 = 38.0; + let (dx, dy) = match delta { + winit::event::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT), + winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32), + }; + + self.scroll_offset += LayoutVector2D::new(dx, dy); + + txn.set_scroll_offsets( + ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), + vec![SampledScrollOffset { + offset: self.scroll_offset, + generation: APZScrollGeneration::default(), + }], + ); + + txn.generate_frame(0, RenderReasons::empty()); + } + winit::event::WindowEvent::MouseInput { .. } => { + let results = api.hit_test( + document_id, + 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_offset: LayoutVector2D::zero(), + }; + boilerplate::main_wrapper(&mut app, None); +} |