summaryrefslogtreecommitdiffstats
path: root/gfx/wr/examples/scrolling.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/examples/scrolling.rs')
-rw-r--r--gfx/wr/examples/scrolling.rs298
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);
+}