|
|
|
use std::collections::VecDeque;
|
|
|
|
|
|
|
|
use skulpin::skia_safe::canvas::{SaveLayerRec, SrcRectConstraint};
|
|
|
|
use skulpin::skia_safe::gpu::SurfaceOrigin;
|
|
|
|
use skulpin::skia_safe::{
|
|
|
|
image_filters::blur, BlendMode, Budgeted, Canvas, Color, Image, ImageInfo, Paint, Point, Rect,
|
|
|
|
Surface,
|
|
|
|
};
|
|
|
|
|
|
|
|
use super::animation_utils::*;
|
|
|
|
use super::{Renderer, RendererSettings};
|
|
|
|
use crate::editor::WindowDrawCommand;
|
|
|
|
use crate::redraw_scheduler::REDRAW_SCHEDULER;
|
|
|
|
|
|
|
|
fn build_window_surface(
|
|
|
|
parent_canvas: &mut Canvas,
|
|
|
|
pixel_width: i32,
|
|
|
|
pixel_height: i32,
|
|
|
|
) -> Surface {
|
|
|
|
let dimensions = (pixel_width, pixel_height);
|
|
|
|
let mut context = parent_canvas.gpu_context().unwrap();
|
|
|
|
let budgeted = Budgeted::Yes;
|
|
|
|
let parent_image_info = parent_canvas.image_info();
|
|
|
|
let image_info = ImageInfo::new(
|
|
|
|
dimensions,
|
|
|
|
parent_image_info.color_type(),
|
|
|
|
parent_image_info.alpha_type(),
|
|
|
|
parent_image_info.color_space(),
|
|
|
|
);
|
|
|
|
let surface_origin = SurfaceOrigin::TopLeft;
|
|
|
|
Surface::new_render_target(
|
|
|
|
&mut context,
|
|
|
|
budgeted,
|
|
|
|
&image_info,
|
|
|
|
None,
|
|
|
|
surface_origin,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.expect("Could not create surface")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn build_window_surface_with_grid_size(
|
|
|
|
parent_canvas: &mut Canvas,
|
|
|
|
renderer: &Renderer,
|
|
|
|
grid_width: u64,
|
|
|
|
grid_height: u64,
|
|
|
|
scaling: f32,
|
|
|
|
) -> Surface {
|
|
|
|
let pixel_width = (grid_width as f32 * renderer.font_width / scaling) as i32;
|
|
|
|
let pixel_height = (grid_height as f32 * renderer.font_height / scaling) as i32;
|
|
|
|
build_window_surface(parent_canvas, pixel_width, pixel_height)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn build_background_window_surface(
|
|
|
|
parent_canvas: &mut Canvas,
|
|
|
|
renderer: &Renderer,
|
|
|
|
grid_width: u64,
|
|
|
|
grid_height: u64,
|
|
|
|
scaling: f32,
|
|
|
|
) -> Surface {
|
|
|
|
let mut surface = build_window_surface_with_grid_size(
|
|
|
|
parent_canvas,
|
|
|
|
renderer,
|
|
|
|
grid_width,
|
|
|
|
grid_height,
|
|
|
|
scaling,
|
|
|
|
);
|
|
|
|
let canvas = surface.canvas();
|
|
|
|
canvas.clear(renderer.get_default_background());
|
|
|
|
surface
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SnapshotPair {
|
|
|
|
background: Image,
|
|
|
|
foreground: Image,
|
|
|
|
top_line: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SurfacePair {
|
|
|
|
background: Surface,
|
|
|
|
foreground: Surface,
|
|
|
|
pub top_line: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SurfacePair {
|
|
|
|
fn new(
|
|
|
|
parent_canvas: &mut Canvas,
|
|
|
|
renderer: &Renderer,
|
|
|
|
grid_width: u64,
|
|
|
|
grid_height: u64,
|
|
|
|
top_line: f32,
|
|
|
|
scaling: f32,
|
|
|
|
) -> SurfacePair {
|
|
|
|
let background = build_background_window_surface(
|
|
|
|
parent_canvas,
|
|
|
|
renderer,
|
|
|
|
grid_width,
|
|
|
|
grid_height,
|
|
|
|
scaling,
|
|
|
|
);
|
|
|
|
let foreground = build_window_surface_with_grid_size(
|
|
|
|
parent_canvas,
|
|
|
|
renderer,
|
|
|
|
grid_width,
|
|
|
|
grid_height,
|
|
|
|
scaling,
|
|
|
|
);
|
|
|
|
|
|
|
|
SurfacePair {
|
|
|
|
background,
|
|
|
|
foreground,
|
|
|
|
top_line,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn snapshot(&mut self) -> SnapshotPair {
|
|
|
|
let background = self.background.image_snapshot();
|
|
|
|
let foreground = self.foreground.image_snapshot();
|
|
|
|
SnapshotPair {
|
|
|
|
background,
|
|
|
|
foreground,
|
|
|
|
top_line: self.top_line,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RenderedWindow {
|
|
|
|
snapshots: VecDeque<SnapshotPair>,
|
|
|
|
pub current_surfaces: SurfacePair,
|
|
|
|
|
|
|
|
pub id: u64,
|
|
|
|
pub hidden: bool,
|
|
|
|
pub floating: bool,
|
|
|
|
|
|
|
|
pub grid_width: u64,
|
|
|
|
pub grid_height: u64,
|
|
|
|
|
|
|
|
grid_start_position: Point,
|
|
|
|
pub grid_current_position: Point,
|
|
|
|
grid_destination: Point,
|
|
|
|
position_t: f32,
|
|
|
|
|
|
|
|
start_scroll: f32,
|
|
|
|
pub current_scroll: f32,
|
|
|
|
scroll_destination: f32,
|
|
|
|
scroll_t: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct WindowDrawDetails {
|
|
|
|
pub id: u64,
|
|
|
|
pub region: Rect,
|
|
|
|
pub floating: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderedWindow {
|
|
|
|
pub fn new(
|
|
|
|
parent_canvas: &mut Canvas,
|
|
|
|
renderer: &Renderer,
|
|
|
|
id: u64,
|
|
|
|
grid_position: Point,
|
|
|
|
grid_width: u64,
|
|
|
|
grid_height: u64,
|
|
|
|
scaling: f32,
|
|
|
|
) -> RenderedWindow {
|
|
|
|
let current_surfaces = SurfacePair::new(
|
|
|
|
parent_canvas,
|
|
|
|
renderer,
|
|
|
|
grid_width,
|
|
|
|
grid_height,
|
|
|
|
0.0,
|
|
|
|
scaling,
|
|
|
|
);
|
|
|
|
|
|
|
|
RenderedWindow {
|
|
|
|
snapshots: VecDeque::new(),
|
|
|
|
current_surfaces,
|
|
|
|
id,
|
|
|
|
hidden: false,
|
|
|
|
floating: false,
|
|
|
|
|
|
|
|
grid_width,
|
|
|
|
grid_height,
|
|
|
|
|
|
|
|
grid_start_position: grid_position,
|
|
|
|
grid_current_position: grid_position,
|
|
|
|
grid_destination: grid_position,
|
|
|
|
position_t: 2.0, // 2.0 is out of the 0.0 to 1.0 range and stops animation
|
|
|
|
|
|
|
|
start_scroll: 0.0,
|
|
|
|
current_scroll: 0.0,
|
|
|
|
scroll_destination: 0.0,
|
|
|
|
scroll_t: 2.0, // 2.0 is out of the 0.0 to 1.0 range and stops animation
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pixel_region(&self, font_width: f32, font_height: f32) -> Rect {
|
|
|
|
let current_pixel_position = Point::new(
|
|
|
|
self.grid_current_position.x * font_width,
|
|
|
|
self.grid_current_position.y * font_height,
|
|
|
|
);
|
|
|
|
|
|
|
|
let image_width = (self.grid_width as f32 * font_width) as i32;
|
|
|
|
let image_height = (self.grid_height as f32 * font_height) as i32;
|
|
|
|
|
|
|
|
Rect::from_point_and_size(current_pixel_position, (image_width, image_height))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update(&mut self, settings: &RendererSettings, dt: f32) -> bool {
|
|
|
|
let mut animating = false;
|
|
|
|
|
|
|
|
{
|
|
|
|
if (self.position_t - 1.0).abs() < std::f32::EPSILON {
|
|
|
|
// We are at destination, move t out of 0-1 range to stop the animation
|
|
|
|
self.position_t = 2.0;
|
|
|
|
} else {
|
|
|
|
animating = true;
|
|
|
|
self.position_t =
|
|
|
|
(self.position_t + dt / settings.position_animation_length).min(1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.grid_current_position = ease_point(
|
|
|
|
ease_out_expo,
|
|
|
|
self.grid_start_position,
|
|
|
|
self.grid_destination,
|
|
|
|
self.position_t,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
if (self.scroll_t - 1.0).abs() < std::f32::EPSILON {
|
|
|
|
// We are at destination, move t out of 0-1 range to stop the animation
|
|
|
|
self.scroll_t = 2.0;
|
|
|
|
self.snapshots.clear();
|
|
|
|
} else {
|
|
|
|
animating = true;
|
|
|
|
self.scroll_t = (self.scroll_t + dt / settings.scroll_animation_length).min(1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.current_scroll = ease(
|
|
|
|
ease_out_expo,
|
|
|
|
self.start_scroll,
|
|
|
|
self.scroll_destination,
|
|
|
|
self.scroll_t,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
animating
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw(
|
|
|
|
&mut self,
|
|
|
|
root_canvas: &mut Canvas,
|
|
|
|
settings: &RendererSettings,
|
|
|
|
default_background: Color,
|
|
|
|
font_width: f32,
|
|
|
|
font_height: f32,
|
|
|
|
dt: f32,
|
|
|
|
) -> WindowDrawDetails {
|
|
|
|
if self.update(settings, dt) {
|
|
|
|
REDRAW_SCHEDULER.queue_next_frame();
|
|
|
|
}
|
|
|
|
|
|
|
|
let pixel_region = self.pixel_region(font_width, font_height);
|
|
|
|
|
|
|
|
root_canvas.save();
|
|
|
|
root_canvas.clip_rect(&pixel_region, None, Some(false));
|
|
|
|
|
|
|
|
if self.floating && settings.floating_blur {
|
|
|
|
let blur = blur((2.0, 2.0), None, None, None).unwrap();
|
|
|
|
let save_layer_rec = SaveLayerRec::default()
|
|
|
|
.backdrop(&blur)
|
|
|
|
.bounds(&pixel_region);
|
|
|
|
|
|
|
|
root_canvas.save_layer(&save_layer_rec);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut paint = Paint::default();
|
|
|
|
// We want each surface to overwrite the one underneath and will use layers to ensure
|
|
|
|
// only lower priority surfaces will get clobbered and not the underlying windows
|
|
|
|
paint.set_blend_mode(BlendMode::Src);
|
|
|
|
|
|
|
|
{
|
|
|
|
// Save layer so that setting the blend mode doesn't effect the blur
|
|
|
|
root_canvas.save_layer(&SaveLayerRec::default());
|
|
|
|
let mut a = 255;
|
|
|
|
if self.floating {
|
|
|
|
a = (settings.floating_opacity.min(1.0).max(0.0) * 255.0) as u8;
|
|
|
|
}
|
|
|
|
|
|
|
|
paint.set_color(default_background.with_a(a));
|
|
|
|
root_canvas.draw_rect(pixel_region, &paint);
|
|
|
|
|
|
|
|
paint.set_color(Color::from_argb(a, 255, 255, 255));
|
|
|
|
|
|
|
|
// Draw background scrolling snapshots
|
|
|
|
for snapshot_pair in self.snapshots.iter_mut().rev() {
|
|
|
|
let scroll_offset =
|
|
|
|
snapshot_pair.top_line * font_height - self.current_scroll * font_height;
|
|
|
|
let background_snapshot = &mut snapshot_pair.background;
|
|
|
|
root_canvas.draw_image_rect(
|
|
|
|
background_snapshot,
|
|
|
|
None,
|
|
|
|
pixel_region.with_offset((0.0, scroll_offset)),
|
|
|
|
&paint,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// Draw background
|
|
|
|
let scroll_offset =
|
|
|
|
self.current_surfaces.top_line * font_height - self.current_scroll * font_height;
|
|
|
|
let background_snapshot = self.current_surfaces.background.image_snapshot();
|
|
|
|
root_canvas.draw_image_rect(
|
|
|
|
background_snapshot,
|
|
|
|
None,
|
|
|
|
pixel_region.with_offset((0.0, scroll_offset)),
|
|
|
|
&paint,
|
|
|
|
);
|
|
|
|
|
|
|
|
root_canvas.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Save layer so that text may safely overwrite images underneath
|
|
|
|
root_canvas.save_layer(&SaveLayerRec::default());
|
|
|
|
|
|
|
|
// Draw foreground scrolling snapshots
|
|
|
|
for snapshot_pair in self.snapshots.iter_mut().rev() {
|
|
|
|
let scroll_offset =
|
|
|
|
snapshot_pair.top_line * font_height - self.current_scroll * font_height;
|
|
|
|
let foreground_snapshot = &mut snapshot_pair.foreground;
|
|
|
|
root_canvas.draw_image_rect(
|
|
|
|
foreground_snapshot,
|
|
|
|
None,
|
|
|
|
pixel_region.with_offset((0.0, scroll_offset)),
|
|
|
|
&paint,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw foreground
|
|
|
|
let scroll_offset =
|
|
|
|
self.current_surfaces.top_line * font_height - self.current_scroll * font_height;
|
|
|
|
let foreground_snapshot = self.current_surfaces.foreground.image_snapshot();
|
|
|
|
root_canvas.draw_image_rect(
|
|
|
|
foreground_snapshot,
|
|
|
|
None,
|
|
|
|
pixel_region.with_offset((0.0, scroll_offset)),
|
|
|
|
&paint,
|
|
|
|
);
|
|
|
|
root_canvas.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.floating {
|
|
|
|
root_canvas.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
root_canvas.restore();
|
|
|
|
|
|
|
|
WindowDrawDetails {
|
|
|
|
id: self.id,
|
|
|
|
region: pixel_region,
|
|
|
|
floating: self.floating,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn handle_window_draw_command(
|
|
|
|
mut self,
|
|
|
|
renderer: &mut Renderer,
|
|
|
|
draw_command: WindowDrawCommand,
|
|
|
|
scaling: f32,
|
|
|
|
) -> Self {
|
|
|
|
match draw_command {
|
|
|
|
WindowDrawCommand::Position {
|
|
|
|
grid_left,
|
|
|
|
grid_top,
|
|
|
|
width: grid_width,
|
|
|
|
height: grid_height,
|
|
|
|
floating,
|
|
|
|
} => {
|
|
|
|
let new_destination: Point = (grid_left as f32, grid_top as f32).into();
|
|
|
|
|
|
|
|
if self.grid_destination != new_destination {
|
|
|
|
if self.grid_start_position.x.abs() > f32::EPSILON
|
|
|
|
|| self.grid_start_position.y.abs() > f32::EPSILON
|
|
|
|
{
|
|
|
|
self.position_t = 0.0; // Reset animation as we have a new destination.
|
|
|
|
self.grid_start_position = self.grid_current_position;
|
|
|
|
self.grid_destination = new_destination;
|
|
|
|
} else {
|
|
|
|
// We don't want to animate since the window is animating out of the start location,
|
|
|
|
// so we set t to 2.0 to stop animations.
|
|
|
|
self.position_t = 2.0;
|
|
|
|
self.grid_start_position = new_destination;
|
|
|
|
self.grid_destination = new_destination;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if grid_width != self.grid_width || grid_height != self.grid_height {
|
|
|
|
{
|
|
|
|
let mut old_background = self.current_surfaces.background;
|
|
|
|
self.current_surfaces.background = build_background_window_surface(
|
|
|
|
old_background.canvas(),
|
|
|
|
&renderer,
|
|
|
|
grid_width,
|
|
|
|
grid_height,
|
|
|
|
scaling,
|
|
|
|
);
|
|
|
|
old_background.draw(
|
|
|
|
self.current_surfaces.background.canvas(),
|
|
|
|
(0.0, 0.0),
|
|
|
|
None,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let mut old_foreground = self.current_surfaces.foreground;
|
|
|
|
self.current_surfaces.foreground = build_window_surface_with_grid_size(
|
|
|
|
old_foreground.canvas(),
|
|
|
|
&renderer,
|
|
|
|
grid_width,
|
|
|
|
grid_height,
|
|
|
|
scaling,
|
|
|
|
);
|
|
|
|
old_foreground.draw(
|
|
|
|
self.current_surfaces.foreground.canvas(),
|
|
|
|
(0.0, 0.0),
|
|
|
|
None,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.grid_width = grid_width;
|
|
|
|
self.grid_height = grid_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.floating = floating;
|
|
|
|
|
|
|
|
if self.hidden {
|
|
|
|
self.hidden = false;
|
|
|
|
self.position_t = 2.0; // We don't want to animate since the window is becoming visible, so we set t to 2.0 to stop animations.
|
|
|
|
self.grid_start_position = new_destination;
|
|
|
|
self.grid_destination = new_destination;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WindowDrawCommand::Cell {
|
|
|
|
text,
|
|
|
|
cell_width,
|
|
|
|
window_left,
|
|
|
|
window_top,
|
|
|
|
style,
|
|
|
|
} => {
|
|
|
|
let grid_position = (window_left, window_top);
|
|
|
|
|
|
|
|
{
|
|
|
|
let canvas = self.current_surfaces.background.canvas();
|
|
|
|
canvas.save();
|
|
|
|
canvas.scale((1.0 / scaling, 1.0 / scaling));
|
|
|
|
renderer.draw_background(canvas, grid_position, cell_width, &style);
|
|
|
|
canvas.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let canvas = self.current_surfaces.foreground.canvas();
|
|
|
|
canvas.save();
|
|
|
|
canvas.scale((1.0 / scaling, 1.0 / scaling));
|
|
|
|
renderer.draw_foreground(canvas, &text, grid_position, cell_width, &style);
|
|
|
|
canvas.restore();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WindowDrawCommand::Scroll {
|
|
|
|
top,
|
|
|
|
bot,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
rows,
|
|
|
|
cols,
|
|
|
|
} => {
|
|
|
|
let scrolled_region = Rect::new(
|
|
|
|
left as f32 * renderer.font_width / scaling,
|
|
|
|
top as f32 * renderer.font_height / scaling,
|
|
|
|
right as f32 * renderer.font_width / scaling,
|
|
|
|
bot as f32 * renderer.font_height / scaling,
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut translated_region = scrolled_region;
|
|
|
|
translated_region.offset((
|
|
|
|
-cols as f32 * renderer.font_width / scaling,
|
|
|
|
-rows as f32 * renderer.font_height / scaling,
|
|
|
|
));
|
|
|
|
|
|
|
|
{
|
|
|
|
let background_snapshot = self.current_surfaces.background.image_snapshot();
|
|
|
|
let background_canvas = self.current_surfaces.background.canvas();
|
|
|
|
|
|
|
|
background_canvas.save();
|
|
|
|
background_canvas.clip_rect(scrolled_region, None, Some(false));
|
|
|
|
|
|
|
|
background_canvas.draw_image_rect(
|
|
|
|
background_snapshot,
|
|
|
|
Some((&scrolled_region, SrcRectConstraint::Fast)),
|
|
|
|
translated_region,
|
|
|
|
&renderer.paint,
|
|
|
|
);
|
|
|
|
|
|
|
|
background_canvas.restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let foreground_snapshot = self.current_surfaces.foreground.image_snapshot();
|
|
|
|
let foreground_canvas = self.current_surfaces.foreground.canvas();
|
|
|
|
|
|
|
|
foreground_canvas.save();
|
|
|
|
foreground_canvas.clip_rect(scrolled_region, None, Some(false));
|
|
|
|
|
|
|
|
foreground_canvas.draw_image_rect(
|
|
|
|
foreground_snapshot,
|
|
|
|
Some((&scrolled_region, SrcRectConstraint::Fast)),
|
|
|
|
translated_region,
|
|
|
|
&renderer.paint,
|
|
|
|
);
|
|
|
|
|
|
|
|
foreground_canvas.restore();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WindowDrawCommand::Clear => {
|
|
|
|
self.current_surfaces.background = build_background_window_surface(
|
|
|
|
self.current_surfaces.background.canvas(),
|
|
|
|
&renderer,
|
|
|
|
self.grid_width,
|
|
|
|
self.grid_height,
|
|
|
|
scaling,
|
|
|
|
);
|
|
|
|
|
|
|
|
self.current_surfaces.foreground = build_window_surface_with_grid_size(
|
|
|
|
self.current_surfaces.foreground.canvas(),
|
|
|
|
&renderer,
|
|
|
|
self.grid_width,
|
|
|
|
self.grid_height,
|
|
|
|
scaling,
|
|
|
|
);
|
|
|
|
|
|
|
|
self.snapshots.clear();
|
|
|
|
}
|
|
|
|
WindowDrawCommand::Show => {
|
|
|
|
if self.hidden {
|
|
|
|
self.hidden = false;
|
|
|
|
self.position_t = 2.0; // We don't want to animate since the window is becoming visible, so we set t to 2.0 to stop animations.
|
|
|
|
self.grid_start_position = self.grid_destination;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WindowDrawCommand::Hide => self.hidden = true,
|
|
|
|
WindowDrawCommand::Viewport { top_line, .. } => {
|
|
|
|
if (self.current_surfaces.top_line - top_line as f32).abs() > std::f32::EPSILON {
|
|
|
|
let new_snapshot = self.current_surfaces.snapshot();
|
|
|
|
self.snapshots.push_back(new_snapshot);
|
|
|
|
|
|
|
|
if self.snapshots.len() > 5 {
|
|
|
|
self.snapshots.pop_front();
|
|
|
|
}
|
|
|
|
|
|
|
|
self.current_surfaces.top_line = top_line as f32;
|
|
|
|
|
|
|
|
// Set new target viewport position and initialize animation timer
|
|
|
|
self.start_scroll = self.current_scroll;
|
|
|
|
self.scroll_destination = top_line as f32;
|
|
|
|
self.scroll_t = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
};
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|