Skip to content
This repository has been archived by the owner on Dec 28, 2021. It is now read-only.

Scrollbar and ScrollArea #1614

Merged
merged 12 commits into from
Jul 5, 2021
1 change: 1 addition & 0 deletions src/rust/ensogl/example/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub mod easing_animator;
pub mod glyph_system;
pub mod list_view;
pub mod mouse_events;
pub mod scroll_area;
pub mod shape_system;
pub mod slider;
pub mod sprite_system;
Expand Down
86 changes: 86 additions & 0 deletions src/rust/ensogl/example/src/scroll_area.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! A debug scene which shows the scroll area.

use crate::prelude::*;
use wasm_bindgen::prelude::*;

use ensogl_core::application::Application;
use ensogl_core::data::color;
use ensogl_core::display::object::ObjectOps;
use ensogl_core::system::web;
use ensogl_text_msdf_sys::run_once_initialized;
use ensogl_theme as theme;
use ensogl_gui_components::scroll_area::ScrollArea;
use ensogl_core::display::shape::{Circle, Rect, ShapeSystem};
use ensogl_core::display::shape::PixelDistance;
use ensogl_core::display::shape::ShapeOps;
use ensogl_core::display::Sprite;



// ===================
// === Entry Point ===
// ===================

/// An entry point.
#[wasm_bindgen]
pub fn entry_point_scroll_area() {
web::forward_panic_hook_to_console();
web::set_stack_trace_limit();
run_once_initialized(|| {
let app = Application::new(&web::get_html_element_by_id("root").unwrap());
init(&app);
mem::forget(app);
});
}



// ========================
// === Init Application ===
// ========================

fn init(app:&Application) {
theme::builtin::dark::register(&app);
theme::builtin::light::register(&app);
theme::builtin::light::enable(&app);

let scene = app.display.scene();
scene.camera().set_position_xy(Vector2(100.0,-100.0));


// === Background ===

let background_color = color::Rgba::new(0.9,0.9,0.9,1.0);
let background_size = (200.px(), 200.px());
let background_shape = Rect(background_size).corners_radius(5.5.px()).fill(background_color);
let background_system = ShapeSystem::new(scene,background_shape);
let background: Sprite = background_system.new_instance();
scene.add_child(&background);
background.size.set(Vector2::new(200.0,200.0));
background.set_position_x(100.0);
background.set_position_y(-100.0);
std::mem::forget(background);


// === Scroll Area ===

let scroll_area = ScrollArea::new(&app);
app.display.add_child(&scroll_area);
scroll_area.resize(Vector2(200.0,200.0));
scroll_area.set_content_width(300.0);
scroll_area.set_content_height(1000.0);


// === Content ===

let sprite_system = ShapeSystem::new(scene,&Circle(50.px()));
let sprite: Sprite = sprite_system.new_instance();
scroll_area.content.add_child(&sprite);
sprite.size.set(Vector2::new(100.0,100.0));
sprite.set_position_x(100.0);
sprite.set_position_y(-100.0);
std::mem::forget(sprite);


std::mem::forget(scroll_area);
}
4 changes: 3 additions & 1 deletion src/rust/ensogl/lib/components/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ pub mod component;
pub mod drop_down_menu;
pub mod label;
pub mod list_view;
pub mod shadow;
pub mod scroll_area;
pub mod scrollbar;
pub mod selector;
pub mod shadow;
pub mod toggle_button;

/// Commonly used types and functions.
Expand Down
162 changes: 162 additions & 0 deletions src/rust/ensogl/lib/components/src/scroll_area.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//! This module provides the [`ScrollArea`] component.

use crate::prelude::*;

use crate::scrollbar;
use crate::scrollbar::Scrollbar;

use enso_frp as frp;
use ensogl_core::display;
use ensogl_core::display::object::ObjectOps;
use ensogl_core::application::Application;
use ensogl_core::control::io::mouse;
use ensogl_core::control::callback;



// ===========
// === Frp ===
// ===========

ensogl_core::define_endpoints! {
Input {
/// Set the width and height in px.
resize (Vector2),
/// Set the content width in px. Affects how far one can scroll horizontally.
set_content_width (f32),
/// Set the content height in px. Affects how far one can scroll vertically.
set_content_height (f32),
/// Scrolls smoothly to the given x coordinate.
scroll_to_x (f32),
/// Scrolls smoothly to the given y coordinate.
scroll_to_y (f32),
/// Jumps instantly to the given x coordinate, without animation.
jump_to_x (f32),
/// Jumps instantly to the given y coordinate, without animation.
jump_to_y (f32),
}
Output {
/// The content's x coordinate at the left edge of the area.
scroll_position_x (f32),
/// The content's y coordinate at the top edge of the area.
scroll_position_y (f32),
}
}



// ===================
// === Scroll Area ===
// ===================

/// This struct provides a scroll area component. It displays two scrollbars, for horizontal and
/// vertical scrolling. Content can be added to the `content` attribute. The content size has to be
/// set through `set_content_height` and `set_content_width`. The component is anchored at the top
/// left corner. All scroll coordinates describe the point of the `content` object at that corner.
/// The scrollbars are only active when the content is actually larger than the viewport on the
/// respective axis.
#[derive(Debug,Clone,CloneRef)]
pub struct ScrollArea {
/// All objects that should be inside the scroll area and affected by the scrolling, have to be
/// added as children to `content`.
pub content : display::object::Instance,
display_object : display::object::Instance,
h_scrollbar : Scrollbar,
v_scrollbar : Scrollbar,
scroll_handler_handle : callback::Handle,
frp : Frp,
}

impl Deref for ScrollArea {
type Target = Frp;

fn deref(&self) -> &Self::Target {
&self.frp
}
}

impl display::Object for ScrollArea {
fn display_object(&self) -> &display::object::Instance {
&self.display_object
}
}

impl ScrollArea {
/// Create a new scroll area for use in the given application.
pub fn new(app:&Application) -> ScrollArea {
let scene = app.display.scene();
let logger = Logger::new("ScrollArea");
let display_object = display::object::Instance::new(&logger);

let content = display::object::Instance::new(&logger);
display_object.add_child(&content);

let h_scrollbar = Scrollbar::new(&app);
display_object.add_child(&h_scrollbar);

let v_scrollbar = Scrollbar::new(&app);
display_object.add_child(&v_scrollbar);
v_scrollbar.set_rotation_z(-90.0_f32.to_radians());

let frp = Frp::new();
let network = &frp.network;

frp::extend! { network

// === Size and Position ===

h_scrollbar.set_max <+ frp.set_content_width;
v_scrollbar.set_max <+ frp.set_content_height;
h_scrollbar.set_thumb_size <+ frp.resize.map(|size| size.x);
v_scrollbar.set_thumb_size <+ frp.resize.map(|size| size.y);
h_scrollbar.set_length <+ frp.resize.map(|size| size.x);
v_scrollbar.set_length <+ frp.resize.map(|size| size.y);

eval frp.resize([h_scrollbar,v_scrollbar](size) {
h_scrollbar.set_position_y(-size.y+scrollbar::WIDTH/2.0);
v_scrollbar.set_position_x(size.x-scrollbar::WIDTH/2.0);
h_scrollbar.set_position_x(size.x/2.0);
v_scrollbar.set_position_y(-size.y/2.0);
});


// === Scrolling ===

h_scrollbar.scroll_to <+ frp.scroll_to_x;
v_scrollbar.scroll_to <+ frp.scroll_to_y;
h_scrollbar.jump_to <+ frp.jump_to_x;
v_scrollbar.jump_to <+ frp.jump_to_y;

frp.source.scroll_position_x <+ h_scrollbar.thumb_position.map(|x| -x);
frp.source.scroll_position_y <+ v_scrollbar.thumb_position;

eval frp.scroll_position_x((&pos) content.set_position_x(pos));
eval frp.scroll_position_y((&pos) content.set_position_y(pos));
}


// === Mouse Wheel ===

let mouse = &scene.mouse;
frp::extend! { network
hovering <- all_with(&mouse.frp.position,&frp.resize,
f!([scene,display_object](&pos,&size) {
let local_pos = scene.screen_to_object_space(&display_object,pos);
(0.0..=size.x).contains(&local_pos.x) && (-size.y..=0.0).contains(&local_pos.y)
}));
hovering <- hovering.sampler();
}

let mouse_manager = &mouse.mouse_manager;
let scroll_handler = f!([v_scrollbar,h_scrollbar](event:&mouse::OnWheel)
if hovering.value() {
h_scrollbar.scroll_by(event.delta_x() as f32);
v_scrollbar.scroll_by(event.delta_y() as f32);
}
);
let scroll_handler_handle = mouse_manager.on_wheel.add(scroll_handler);


ScrollArea {content,display_object,h_scrollbar,v_scrollbar,scroll_handler_handle,frp}
}
}
Loading