tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

scrolling.rs (11109B)


      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 extern crate winit;
     10 
     11 #[path = "common/boilerplate.rs"]
     12 mod boilerplate;
     13 
     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;
     20 
     21 
     22 const EXT_SCROLL_ID_ROOT: u64 = 1;
     23 const EXT_SCROLL_ID_CONTENT: u64 = 2;
     24 
     25 struct App {
     26    cursor_position: WorldPoint,
     27    scroll_offset: LayoutVector2D,
     28 }
     29 
     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        );
     46 
     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            };
     71 
     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));
     83 
     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));
     94 
     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));
    109 
    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            };
    127 
    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));
    142 
    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));
    154 
    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                None,
    168            );
    169 
    170            let info = CommonItemProperties::new(
    171                (50, 350).by(50, 50),
    172                SpaceAndClipInfo {
    173                    spatial_id: sticky_id,
    174                    clip_chain_id: space_and_clip2.clip_chain_id,
    175                },
    176            );
    177            builder.push_hit_test(
    178                info.clip_rect,
    179                ClipChainId::INVALID,
    180                info.spatial_id,
    181                info.flags,
    182                (0, 6)
    183            );
    184            builder.push_rect(
    185                &info,
    186                info.clip_rect,
    187                ColorF::new(0.5, 0.5, 1.0, 1.0),
    188            );
    189 
    190            // just for good measure add another teal square further down and to
    191            // the right, which can be scrolled into view by the user
    192            let info = CommonItemProperties::new(
    193                (250, 350).to(300, 400),
    194                space_and_clip2,
    195            );
    196            builder.push_hit_test(
    197                info.clip_rect,
    198                ClipChainId::INVALID,
    199                info.spatial_id,
    200                info.flags,
    201                (0, 7)
    202            );
    203            builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0));
    204 
    205            builder.pop_stacking_context();
    206        }
    207 
    208        builder.pop_stacking_context();
    209    }
    210 
    211    fn on_event(
    212        &mut self,
    213        event: winit::event::WindowEvent,
    214        window: &winit::window::Window,
    215        api: &mut RenderApi,
    216        document_id: DocumentId,
    217    ) -> bool {
    218        let mut txn = Transaction::new();
    219        match event {
    220            winit::event::WindowEvent::KeyboardInput {
    221                input: winit::event::KeyboardInput {
    222                    state: winit::event::ElementState::Pressed,
    223                    virtual_keycode: Some(key),
    224                    ..
    225                },
    226                ..
    227            } => {
    228                let offset = match key {
    229                    winit::event::VirtualKeyCode::Down => Some(LayoutVector2D::new(0.0, -10.0)),
    230                    winit::event::VirtualKeyCode::Up => Some(LayoutVector2D::new(0.0, 10.0)),
    231                    winit::event::VirtualKeyCode::Right => Some(LayoutVector2D::new(-10.0, 0.0)),
    232                    winit::event::VirtualKeyCode::Left => Some(LayoutVector2D::new(10.0, 0.0)),
    233                    _ => None,
    234                };
    235 
    236                if let Some(offset) = offset {
    237                    self.scroll_offset += offset;
    238 
    239                    txn.set_scroll_offsets(
    240                        ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()),
    241                        vec![SampledScrollOffset {
    242                            offset: self.scroll_offset,
    243                            generation: APZScrollGeneration::default(),
    244                        }],
    245                    );
    246                    txn.generate_frame(0, true, false, RenderReasons::empty());
    247                }
    248            }
    249            winit::event::WindowEvent::CursorMoved { position, .. } => {
    250                let pos: LogicalPosition<f32> = position.to_logical(window.scale_factor());
    251                self.cursor_position = WorldPoint::new(pos.x, pos.y);
    252            }
    253            winit::event::WindowEvent::MouseWheel { delta, .. } => {
    254                const LINE_HEIGHT: f32 = 38.0;
    255                let (dx, dy) = match delta {
    256                    winit::event::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
    257                    winit::event::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32),
    258                };
    259 
    260                self.scroll_offset += LayoutVector2D::new(dx, dy);
    261 
    262                txn.set_scroll_offsets(
    263                    ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()),
    264                    vec![SampledScrollOffset {
    265                            offset: self.scroll_offset,
    266                            generation: APZScrollGeneration::default(),
    267                    }],
    268                );
    269 
    270                txn.generate_frame(0, true, false, RenderReasons::empty());
    271            }
    272            winit::event::WindowEvent::MouseInput { .. } => {
    273                let results = api.hit_test(
    274                    document_id,
    275                    self.cursor_position,
    276                );
    277 
    278                println!("Hit test results:");
    279                for item in &results.items {
    280                    println!("  • {:?}", item);
    281                }
    282                println!("");
    283            }
    284            _ => (),
    285        }
    286 
    287        api.send_transaction(document_id, txn);
    288 
    289        false
    290    }
    291 }
    292 
    293 fn main() {
    294    let mut app = App {
    295        cursor_position: WorldPoint::zero(),
    296        scroll_offset: LayoutVector2D::zero(),
    297    };
    298    boilerplate::main_wrapper(&mut app, None);
    299 }