Files
ElyOffice/crates/ely-lok/src/view/scroll.rs
T

182 lines
6.4 KiB
Rust

//! Scrolling the page view (LOK-04).
use gpui::{
Bounds, Context, Pixels, Point, ScrollDelta, ScrollWheelEvent, Window, point, px, size,
};
use super::{LokView, input::Input};
use crate::{Mouse, Rect};
impl LokView {
/// Scrolls by view pixels, clamped to the document (LOK-04).
pub(super) fn scroll_by(&mut self, dx: f32, dy: f32, cx: &mut Context<Self>) {
self.scroll.0 -= self.geom.twips_of(dx);
self.scroll.1 -= self.geom.twips_of(dy);
self.clamp_scroll();
cx.notify();
}
/// Keeps the viewport inside the document (LOK-04).
pub(super) fn clamp_scroll(&mut self) {
let (vw, vh) = (
self.geom.twips_of(self.viewport.0),
self.geom.twips_of(self.viewport.1),
);
let extent = self.extent();
self.scroll.0 = self.scroll.0.clamp(0, (extent.0 - vw).max(0));
self.scroll.1 = self.scroll.1.clamp(0, (extent.1 - vh).max(0));
}
/// Scrolls the wheel in twips, clamped to the document (LOK-04).
pub(super) fn on_scroll(
&mut self,
event: &ScrollWheelEvent,
_window: &mut Window,
cx: &mut Context<Self>,
) {
let (dx, dy) = match event.delta {
ScrollDelta::Pixels(point) => (f32::from(point.x), f32::from(point.y)),
ScrollDelta::Lines(point) => (point.x * 40.0, point.y * 40.0),
};
self.scroll_by(dx, dy, cx);
}
/// A newly selected object, such as a chart, shows.
pub(super) fn follow_graphic(&mut self, frame: Option<Rect>, cx: &mut Context<Self>) {
if let (None, Some(frame), true) = (self.graphic, frame, self.viewport.1 > 0.0) {
self.show(frame, cx);
}
}
/// Scrolls print-twips `rect` into view; frozen parts always show.
pub fn show(&mut self, rect: Rect, cx: &mut Context<Self>) {
let (x, y, w, h) = self.tile_rect(rect);
let (fx, fy) = self.frozen_tiles();
let (vw, vh) = (
self.geom.twips_of(self.viewport.0) - fx,
self.geom.twips_of(self.viewport.1) - fy,
);
// Only a sheet grows where it jumps.
if self.sheet.is_some() {
let need = self.print_of((x + w + vw + fx, y + h + vh + fy));
self.reach = (self.reach.0.max(need.0), self.reach.1.max(need.1));
}
if x >= fx {
self.scroll.0 = least(x - fx, w, self.scroll.0, vw);
}
if y >= fy {
self.scroll.1 = least(y - fy, h, self.scroll.1, vh);
}
self.clamp_scroll();
cx.notify();
}
/// Freezes what lies before `frozen`, in print twips.
pub fn set_frozen(&mut self, frozen: (i32, i32), cx: &mut Context<Self>) {
if self.frozen != frozen {
log::info!("lok: frozen at {frozen:?}");
self.frozen = frozen;
cx.notify();
}
}
/// Scrolls `rect` into view and puts the cursor there.
pub fn reveal(&mut self, rect: Rect, cx: &mut Context<Self>) {
self.show(rect, cx);
// Clicks land in tile twips.
let (x, y, _, h) = self.tile_rect(rect);
log::info!("lok: reveal at {x},{y}");
for kind in [0, 1] {
let (count, buttons, modifiers) = (1, 1, 0);
let y = y + h / 2;
self.send(Input::Mouse(Mouse {
kind,
x,
y,
count,
buttons,
modifiers,
}));
}
}
/// The scroll offset in twips.
pub fn scroll(&self) -> (i32, i32) {
self.scroll
}
/// The document extent the view may scroll over.
pub(super) fn extent(&self) -> (i32, i32) {
// A fitted page is the page, whatever the cursor reached.
if self.fit {
return self.doc_size;
}
self.tile_of((
self.doc_size.0.max(self.reach.0),
self.doc_size.1.max(self.reach.1),
))
}
/// The print-twips document point under a view position (LOK-04).
pub fn doc_point(&self, position: Point<Pixels>) -> (i32, i32) {
self.print_of(self.tile_point(position))
}
/// The tile-twips point under a view position.
pub(super) fn tile_point(&self, position: Point<Pixels>) -> (i32, i32) {
let local = position - self.origin;
let (ax, ay) = self.axes();
(
ax.doc(self.geom.twips_of(f32::from(local.x) - self.spare.0)),
ay.doc(self.geom.twips_of(f32::from(local.y) - self.spare.1)),
)
}
/// The panes of each axis.
pub(super) fn axes(&self) -> (super::panes::Axis, super::panes::Axis) {
let axis = |frozen, scroll| super::panes::Axis { frozen, scroll };
let (fx, fy) = self.frozen_tiles();
(axis(fx, self.scroll.0), axis(fy, self.scroll.1))
}
/// The window point of a print-twips document point (LOK-04).
pub fn window_point(&self, at: (i32, i32)) -> gpui::Point<Pixels> {
self.tile_window_point(self.tile_of(at))
}
/// The window point of a tile-twips point.
pub(super) fn tile_window_point(&self, (x, y): (i32, i32)) -> gpui::Point<Pixels> {
let (ax, ay) = self.axes();
let (x, y) = (self.geom.px_of(ax.show(x)), self.geom.px_of(ay.show(y)));
self.origin + point(px(self.spare.0 + x), px(self.spare.1 + y))
}
/// A document rect in window pixels, panes applied.
pub(super) fn screen_rect(&self, (x, y, w, h): crate::Rect) -> Bounds<Pixels> {
Bounds::from_corners(self.window_point((x, y)), self.window_point((x + w, y + h)))
}
/// A tile-twips rect in window pixels.
pub(super) fn tile_screen_rect(&self, (x, y, w, h): crate::Rect) -> Bounds<Pixels> {
Bounds::from_corners(self.tile_window_point((x, y)), self.tile_window_point((x + w, y + h)))
}
/// The cursor rect in window pixels.
pub fn cursor_bounds(&self) -> Option<Bounds<Pixels>> {
let (x, y, w, h) = self.cursor?;
let origin = self.window_point((x, y));
let end = self.window_point((x + w, y + h));
Some(Bounds {
origin,
size: size((end.x - origin.x).max(px(self.geom.px_of(1))), end.y - origin.y),
})
}
}
/// The scroll nearest `scroll` that shows `start..start + len`.
fn least(start: i32, len: i32, scroll: i32, view: i32) -> i32 {
// A span wider than the view keeps covering it.
let end = start + len - view;
scroll.clamp(start.min(end), start.max(end))
}