diff --git a/lapce-data/src/lib.rs b/lapce-data/src/lib.rs index 60b6affd..18dd17fa 100644 --- a/lapce-data/src/lib.rs +++ b/lapce-data/src/lib.rs @@ -20,7 +20,6 @@ pub mod plugin; pub mod problem; pub mod proxy; -pub mod scroll; pub mod search; pub mod settings; pub mod signature; diff --git a/lapce-data/src/scroll.rs b/lapce-data/src/scroll.rs deleted file mode 100644 index 04f8ae85..00000000 --- a/lapce-data/src/scroll.rs +++ /dev/null @@ -1,1146 +0,0 @@ -use std::time::Duration; -use std::time::Instant; - -use druid::{ - kurbo::{Affine, Point, Rect, Size, Vec2}, - Insets, WidgetId, -}; -use druid::{ - theme, BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, LifeCycle, - LifeCycleCtx, PaintCtx, RenderContext, TimerToken, UpdateCtx, Widget, WidgetPod, -}; - -use crate::{ - command::{LapceUICommand, LAPCE_UI_COMMAND}, - config::{Config, GetConfig, LapceTheme}, -}; - -/// Represents the size and position of a rectangular "viewport" into a larger area. -#[derive(Clone, Copy, Default, Debug, PartialEq)] -pub struct Viewport { - /// The size of the area that we have a viewport into. - pub content_size: Size, - /// The view rectangle. - pub rect: Rect, -} - -impl Viewport { - /// Tries to find a position for the view rectangle that is contained in the content rectangle. - /// - /// If the supplied origin is good, returns it; if it isn't, we try to return the nearest - /// origin that would make the view rectangle contained in the content rectangle. (This will - /// fail if the content is smaller than the view, and we return `0.0` in each dimension where - /// the content is smaller.) - pub fn clamp_view_origin(&self, origin: Point) -> Point { - let x = origin - .x - .min(self.content_size.width - self.rect.width()) - .max(0.0); - let y = origin - .y - .min(self.content_size.height - self.rect.height()) - .max(0.0); - Point::new(x, y) - } - - /// Changes the viewport offset by `delta`, while trying to keep the view rectangle inside the - /// content rectangle. - /// - /// Returns true if the offset actually changed. Even if `delta` is non-zero, the offset might - /// not change. For example, if you try to move the viewport down but it is already at the - /// bottom of the child widget, then the offset will not change and this function will return - /// false. - pub fn pan_by(&mut self, delta: Vec2) -> bool { - self.pan_to(self.rect.origin() + delta) - } - - /// Sets the viewport origin to `pos`, while trying to keep the view rectangle inside the - /// content rectangle. - /// - /// Returns true if the position changed. Note that the valid values for the viewport origin - /// are constrained by the size of the child, and so the origin might not get set to exactly - /// `pos`. - pub fn pan_to(&mut self, origin: Point) -> bool { - let new_origin = self.clamp_view_origin(origin); - if (new_origin - self.rect.origin()).hypot2() > 1e-12 { - self.rect = self.rect.with_origin(new_origin); - true - } else { - false - } - } - - pub fn force_pan_to(&mut self, origin: Point) -> bool { - if (origin - self.rect.origin()).hypot2() > 1e-12 { - self.rect = self.rect.with_origin(origin); - true - } else { - false - } - } -} - -#[derive(Debug, Clone)] -enum ScrollDirection { - #[allow(dead_code)] - Bidirectional, - #[allow(dead_code)] - Vertical, - #[allow(dead_code)] - Horizontal, -} - -/// Minimum length for any scrollbar to be when measured on that -/// scrollbar's primary axis. -pub const SCROLLBAR_MIN_SIZE: f64 = 45.0; - -/// Denotes which scrollbar, if any, is currently being hovered over -/// by the mouse. -#[derive(Debug, Copy, Clone)] -pub enum BarHoveredState { - /// Neither scrollbar is being hovered by the mouse. - None, - /// The vertical scrollbar is being hovered by the mouse. - Vertical, - /// The horizontal scrollbar is being hovered by the mouse. - Horizontal, -} - -impl BarHoveredState { - /// Determines if any scrollbar is currently being hovered by the mouse. - pub fn is_hovered(self) -> bool { - matches!( - self, - BarHoveredState::Vertical | BarHoveredState::Horizontal - ) - } -} - -/// Denotes which scrollbar, if any, is currently being dragged. -#[derive(Debug, Copy, Clone)] -pub enum BarHeldState { - /// Neither scrollbar is being dragged. - None, - /// Vertical scrollbar is being dragged. Contains an `f64` with - /// the initial y-offset of the dragging input. - Vertical(f64), - /// Horizontal scrollbar is being dragged. Contains an `f64` with - /// the initial x-offset of the dragging input. - Horizontal(f64), -} - -#[derive(Clone, Copy, Default, Debug, PartialEq)] -pub struct ViewportNew { - /// The size of the area that we have a viewport into. - pub content_size: Size, - /// The view rectangle. - pub rect: Rect, -} - -impl ViewportNew { - /// Tries to find a position for the view rectangle that is contained in the content rectangle. - /// - /// If the supplied origin is good, returns it; if it isn't, we try to return the nearest - /// origin that would make the view rectangle contained in the content rectangle. (This will - /// fail if the content is smaller than the view, and we return `0.0` in each dimension where - /// the content is smaller.) - pub fn clamp_view_origin(&self, origin: Point) -> Point { - let x = origin - .x - .min(self.content_size.width - self.rect.width()) - .max(0.0); - let y = origin - .y - .min(self.content_size.height - self.rect.height()) - .max(0.0); - Point::new(x, y) - } - - /// Changes the viewport offset by `delta`, while trying to keep the view rectangle inside the - /// content rectangle. - /// - /// Returns true if the offset actually changed. Even if `delta` is non-zero, the offset might - /// not change. For example, if you try to move the viewport down but it is already at the - /// bottom of the child widget, then the offset will not change and this function will return - /// false. - pub fn pan_by(&mut self, delta: Vec2) -> bool { - self.pan_to(self.rect.origin() + delta) - } - - /// Sets the viewport origin to `pos`, while trying to keep the view rectangle inside the - /// content rectangle. - /// - /// Returns true if the position changed. Note that the valid values for the viewport origin - /// are constrained by the size of the child, and so the origin might not get set to exactly - /// `pos`. - pub fn pan_to(&mut self, origin: Point) -> bool { - let new_origin = self.clamp_view_origin(origin); - if (new_origin - self.rect.origin()).hypot2() > 1e-12 { - self.rect = self.rect.with_origin(new_origin); - true - } else { - false - } - } - - pub fn force_pan_to(&mut self, origin: Point) { - self.rect = self.rect.with_origin(origin); - } - - /// Pan the smallest distance that makes the target [`Rect`] visible. - /// - /// If the target rect is larger than viewport size, we will prioritize - /// the region of the target closest to its origin. - pub fn pan_to_visible(&mut self, rect: Rect) -> bool { - /// Given a position and the min and max edges of an axis, - /// return a delta by which to adjust that axis such that the value - /// falls between its edges. - /// - /// if the value already falls between the two edges, return 0.0. - fn closest_on_axis(val: f64, min: f64, max: f64) -> f64 { - assert!(min <= max); - if val > min && val < max { - 0.0 - } else if val <= min { - val - min - } else { - val - max - } - } - - // clamp the target region size to our own size. - // this means we will show the portion of the target region that - // includes the origin. - let target_size = Size::new( - rect.width().min(self.rect.width()), - rect.height().min(self.rect.height()), - ); - let rect = rect.with_size(target_size); - - let x0 = closest_on_axis(rect.min_x(), self.rect.min_x(), self.rect.max_x()); - let x1 = closest_on_axis(rect.max_x(), self.rect.min_x(), self.rect.max_x()); - let y0 = closest_on_axis(rect.min_y(), self.rect.min_y(), self.rect.max_y()); - let y1 = closest_on_axis(rect.max_y(), self.rect.min_y(), self.rect.max_y()); - - let delta_x = if x0.abs() > x1.abs() { x0 } else { x1 }; - let delta_y = if y0.abs() > y1.abs() { y0 } else { y1 }; - let new_origin = self.rect.origin() + Vec2::new(delta_x, delta_y); - self.pan_to(new_origin) - } -} - -pub struct ClipBoxNew { - child: WidgetPod, - port: ViewportNew, - constrain_horizontal: bool, - constrain_vertical: bool, -} - -impl> ClipBoxNew { - /// Creates a new `ClipBox` wrapping `child`. - pub fn new(child: W) -> Self { - Self { - child: WidgetPod::new(child), - port: Default::default(), - constrain_horizontal: false, - constrain_vertical: false, - } - } - - /// Returns a reference to the child widget. - pub fn child(&self) -> &W { - self.child.widget() - } - - /// Returns a mutable reference to the child widget. - pub fn child_mut(&mut self) -> &mut W { - self.child.widget_mut() - } - - /// Returns a the viewport describing this `ClipBox`'s position. - pub fn viewport(&self) -> ViewportNew { - self.port - } - - /// Returns the size of the rectangular viewport into the child widget. - /// To get the position of the viewport, see [`viewport_origin`]. - /// - /// [`viewport_origin`]: struct.ClipBox.html#method.viewport_origin - pub fn viewport_size(&self) -> Size { - self.port.rect.size() - } - - /// Returns the size of the child widget. - pub fn content_size(&self) -> Size { - self.port.content_size - } - - /// Builder-style method for deciding whether to constrain the child horizontally. The default - /// is `false`. See [`constrain_vertical`] for more details. - /// - /// [`constrain_vertical`]: struct.ClipBox.html#constrain_vertical - pub fn constrain_horizontal(mut self, constrain: bool) -> Self { - self.constrain_horizontal = constrain; - self - } - - /// Determine whether to constrain the child horizontally. - /// - /// See [`constrain_vertical`] for more details. - /// - /// [`constrain_vertical`]: struct.ClipBox.html#constrain_vertical - pub fn set_constrain_horizontal(&mut self, constrain: bool) { - self.constrain_horizontal = constrain; - } - - /// Builder-style method for deciding whether to constrain the child vertically. The default - /// is `false`. - /// - /// This setting affects how a `ClipBox` lays out its child. - /// - /// - When it is `false` (the default), the child does receive any upper bound on its height: - /// the idea is that the child can be as tall as it wants, and the viewport will somehow get - /// moved around to see all of it. - /// - When it is `true`, the viewport's maximum height will be passed down as an upper bound on - /// the height of the child, and the viewport will set its own height to be the same as its - /// child's height. - pub fn constrain_vertical(mut self, constrain: bool) -> Self { - self.constrain_vertical = constrain; - self - } - - /// Determine whether to constrain the child vertically. - /// - /// See [`constrain_vertical`] for more details. - /// - /// [`constrain_vertical`]: struct.ClipBox.html#constrain_vertical - pub fn set_constrain_vertical(&mut self, constrain: bool) { - self.constrain_vertical = constrain; - } - - /// Changes the viewport offset by `delta`. - /// - /// Returns true if the offset actually changed. Even if `delta` is non-zero, the offset might - /// not change. For example, if you try to move the viewport down but it is already at the - /// bottom of the child widget, then the offset will not change and this function will return - /// false. - pub fn pan_by(&mut self, delta: Vec2) -> bool { - self.pan_to(self.viewport_origin() + delta) - } - - /// Sets the viewport origin to `pos`. - /// - /// Returns true if the position changed. Note that the valid values for the viewport origin - /// are constrained by the size of the child, and so the origin might not get set to exactly - /// `pos`. - pub fn pan_to(&mut self, origin: Point) -> bool { - if self.port.pan_to(origin) { - self.child - .set_viewport_offset(self.viewport_origin().to_vec2()); - true - } else { - false - } - } - - pub fn force_pan_to(&mut self, origin: Point) { - self.port.force_pan_to(origin); - self.child - .set_viewport_offset(self.viewport_origin().to_vec2()); - } - - /// Adjust the viewport to display as much of the target region as is possible. - /// - /// Returns `true` if the viewport changes. - /// - /// This will move the viewport the smallest distance that fully shows - /// the target region. If the target region is larger than the viewport, - /// we will display the portion that fits, prioritizing the portion closest - /// to the origin. - pub fn pan_to_visible(&mut self, region: Rect) -> bool { - if self.port.pan_to_visible(region) { - self.child - .set_viewport_offset(self.viewport_origin().to_vec2()); - true - } else { - false - } - } - - /// Returns the origin of the viewport rectangle. - pub fn viewport_origin(&self) -> Point { - self.port.rect.origin() - } - - /// Allows this `ClipBox`'s viewport rectangle to be modified. The provided callback function - /// can modify its argument, and when it is done then this `ClipBox` will be modified to have - /// the new viewport rectangle. - pub fn with_port(&mut self, f: F) { - f(&mut self.port); - self.child - .set_viewport_offset(self.viewport_origin().to_vec2()); - } -} - -impl> Widget for ClipBoxNew { - fn event(&mut self, ctx: &mut EventCtx, ev: &Event, data: &mut T, env: &Env) { - let viewport = ctx.size().to_rect(); - let force_event = self.child.is_hot() || self.child.has_active(); - if let Some(child_event) = ev.transform_scroll( - self.viewport_origin().to_vec2(), - viewport, - force_event, - ) { - self.child.event(ctx, &child_event, data, env); - } - } - - fn lifecycle( - &mut self, - ctx: &mut LifeCycleCtx, - ev: &LifeCycle, - data: &T, - env: &Env, - ) { - self.child.lifecycle(ctx, ev, data, env); - } - - fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, data: &T, env: &Env) { - self.child.update(ctx, data, env); - } - - fn layout( - &mut self, - ctx: &mut LayoutCtx, - bc: &BoxConstraints, - data: &T, - env: &Env, - ) -> Size { - bc.debug_check("ClipBox"); - - let content_size = self.child.layout(ctx, bc, data, env); - self.port.content_size = content_size; - self.child.set_origin(ctx, data, env, Point::ORIGIN); - - self.port.rect = self.port.rect.with_size(bc.constrain(content_size)); - let new_offset = self.port.clamp_view_origin(self.viewport_origin()); - self.pan_to(new_offset); - self.viewport_size() - } - - fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { - let viewport = ctx.size().to_rect(); - let offset = self.viewport_origin().to_vec2(); - ctx.with_save(|ctx| { - ctx.clip(viewport); - ctx.transform(Affine::translate(-offset)); - - let mut visible = ctx.region().clone(); - visible += offset; - ctx.with_child_ctx(visible, |ctx| self.child.paint_raw(ctx, data, env)); - }); - } -} - -#[derive(Debug, Copy, Clone)] -pub struct ScrollComponentNew { - /// Current opacity for both scrollbars - pub opacity: f64, - /// ID for the timer which schedules scrollbar fade out - pub timer_id: TimerToken, - /// Which if any scrollbar is currently hovered by the mouse - pub hovered: BarHoveredState, - /// Which if any scrollbar is currently being dragged by the mouse - pub held: BarHeldState, - pub fade_start: Option, -} - -impl Default for ScrollComponentNew { - fn default() -> Self { - Self { - opacity: 0.0, - timer_id: TimerToken::INVALID, - hovered: BarHoveredState::None, - held: BarHeldState::None, - fade_start: None, - } - } -} - -impl ScrollComponentNew { - /// Constructs a new [`ScrollComponent`](struct.ScrollComponent.html) for use. - pub fn new() -> ScrollComponentNew { - Default::default() - } - - /// true if either scrollbar is currently held down/being dragged - pub fn are_bars_held(&self) -> bool { - !matches!(self.held, BarHeldState::None) - } - - /// Makes the scrollbars visible, and resets the fade timer. - pub fn reset_scrollbar_fade(&mut self, request_timer: F, env: &Env) - where - F: FnOnce(Duration) -> TimerToken, - { - self.opacity = env.get(theme::SCROLLBAR_MAX_OPACITY); - self.fade_start = None; - let fade_delay = 500; - let deadline = Duration::from_millis(fade_delay); - self.timer_id = request_timer(deadline); - } - - /// Calculates the paint rect of the vertical scrollbar, or `None` if the vertical scrollbar is - /// not visible. - pub fn calc_vertical_bar_bounds( - &self, - port: &ViewportNew, - env: &Env, - ) -> Option { - let viewport_size = port.rect.size(); - let content_size = port.content_size; - let scroll_offset = port.rect.origin().to_vec2(); - - if viewport_size.height >= content_size.height { - return None; - } - - let bar_width = env.get(theme::SCROLLBAR_WIDTH); - let bar_pad = env.get(theme::SCROLLBAR_PAD); - - let percent_visible = viewport_size.height / content_size.height; - let percent_scrolled = - scroll_offset.y / (content_size.height - viewport_size.height); - - let length = (percent_visible * viewport_size.height).ceil(); - let length = length.max(SCROLLBAR_MIN_SIZE); - - let top_y_offset = - ((viewport_size.height - length) * percent_scrolled).ceil(); - let bottom_y_offset = top_y_offset + length; - - let x0 = scroll_offset.x + viewport_size.width - bar_width - bar_pad; - let y0 = scroll_offset.y + top_y_offset; - - let x1 = scroll_offset.x + viewport_size.width - bar_pad; - let y1 = scroll_offset.y + bottom_y_offset; - - Some(Rect::new(x0, y0, x1, y1)) - } - - /// Calculates the paint rect of the horizontal scrollbar, or `None` if the horizontal - /// scrollbar is not visible. - pub fn calc_horizontal_bar_bounds( - &self, - port: &ViewportNew, - env: &Env, - ) -> Option { - let viewport_size = port.rect.size(); - let content_size = port.content_size; - let scroll_offset = port.rect.origin().to_vec2(); - - if viewport_size.width >= content_size.width { - return None; - } - - let bar_width = env.get(theme::SCROLLBAR_WIDTH); - let bar_pad = env.get(theme::SCROLLBAR_PAD); - - let percent_visible = viewport_size.width / content_size.width; - let percent_scrolled = - scroll_offset.x / (content_size.width - viewport_size.width); - - let length = (percent_visible * viewport_size.width).ceil(); - let length = length.max(SCROLLBAR_MIN_SIZE); - - let horizontal_padding = if viewport_size.height >= content_size.height { - 0.0 - } else { - bar_pad + bar_pad + bar_width - }; - - let left_x_offset = ((viewport_size.width - length - horizontal_padding) - * percent_scrolled) - .ceil(); - let right_x_offset = left_x_offset + length; - - let x0 = scroll_offset.x + left_x_offset; - let y0 = scroll_offset.y + viewport_size.height - bar_width - bar_pad; - - let x1 = scroll_offset.x + right_x_offset; - let y1 = scroll_offset.y + viewport_size.height - bar_pad; - - Some(Rect::new(x0, y0, x1, y1)) - } - - /// Draw scroll bars. - pub fn draw_bars( - &self, - ctx: &mut PaintCtx, - port: &ViewportNew, - env: &Env, - config: &Config, - ) { - let scroll_offset = port.rect.origin().to_vec2(); - if self.opacity <= 0.0 { - return; - } - - let brush = ctx.render_ctx.solid_brush( - config - .get_color_unchecked(LapceTheme::LAPCE_SCROLL_BAR) - .clone() - .with_alpha(self.opacity), - ); - let border_brush = ctx.render_ctx.solid_brush( - config - .get_color_unchecked(LapceTheme::LAPCE_SCROLL_BAR) - .clone() - .with_alpha(self.opacity), - ); - - let _radius = env.get(theme::SCROLLBAR_RADIUS); - let edge_width = env.get(theme::SCROLLBAR_EDGE_WIDTH); - - // Vertical bar - if let Some(bounds) = self.calc_vertical_bar_bounds(port, env) { - let rect = (bounds - scroll_offset).inset(-edge_width / 2.0); - ctx.render_ctx.fill(rect, &brush); - ctx.render_ctx.stroke(rect, &border_brush, edge_width); - } - - // Horizontal bar - if let Some(bounds) = self.calc_horizontal_bar_bounds(port, env) { - let rect = (bounds - scroll_offset).inset(-edge_width / 2.0); - ctx.render_ctx.fill(rect, &brush); - ctx.render_ctx.stroke(rect, &border_brush, edge_width); - } - } - - /// Tests if the specified point overlaps the vertical scrollbar - /// - /// Returns false if the vertical scrollbar is not visible - pub fn point_hits_vertical_bar( - &self, - port: &ViewportNew, - pos: Point, - env: &Env, - ) -> bool { - let viewport_size = port.rect.size(); - let scroll_offset = port.rect.origin().to_vec2(); - - if let Some(mut bounds) = self.calc_vertical_bar_bounds(port, env) { - // Stretch hitbox to edge of widget - bounds.x1 = scroll_offset.x + viewport_size.width; - bounds.contains(pos) - } else { - false - } - } - - /// Tests if the specified point overlaps the horizontal scrollbar - /// - /// Returns false if the horizontal scrollbar is not visible - pub fn point_hits_horizontal_bar( - &self, - port: &ViewportNew, - pos: Point, - env: &Env, - ) -> bool { - let viewport_size = port.rect.size(); - let scroll_offset = port.rect.origin().to_vec2(); - - if let Some(mut bounds) = self.calc_horizontal_bar_bounds(port, env) { - // Stretch hitbox to edge of widget - bounds.y1 = scroll_offset.y + viewport_size.height; - bounds.contains(pos) - } else { - false - } - } - - /// Checks if the event applies to the scroll behavior, uses it, and marks it handled - /// - /// Make sure to call on every event - pub fn event( - &mut self, - port: &mut ViewportNew, - ctx: &mut EventCtx, - event: &Event, - env: &Env, - ) { - let viewport_size = port.rect.size(); - let content_size = port.content_size; - let scroll_offset = port.rect.origin().to_vec2(); - - let scrollbar_is_hovered = match event { - Event::MouseMove(e) | Event::MouseUp(e) | Event::MouseDown(e) => { - let offset_pos = e.pos + scroll_offset; - self.point_hits_vertical_bar(port, offset_pos, env) - || self.point_hits_horizontal_bar(port, offset_pos, env) - } - _ => false, - }; - - if self.are_bars_held() { - // if we're dragging a scrollbar - match event { - Event::MouseMove(event) => { - match self.held { - BarHeldState::Vertical(offset) => { - let scale_y = viewport_size.height / content_size.height; - let bounds = self - .calc_vertical_bar_bounds(port, env) - .unwrap_or(Rect::ZERO); - let mouse_y = event.pos.y + scroll_offset.y; - let delta = mouse_y - bounds.y0 - offset; - port.pan_by(Vec2::new(0f64, (delta / scale_y).ceil())); - ctx.set_handled(); - } - BarHeldState::Horizontal(offset) => { - let scale_x = viewport_size.width / content_size.width; - let bounds = self - .calc_horizontal_bar_bounds(port, env) - .unwrap_or(Rect::ZERO); - let mouse_x = event.pos.x + scroll_offset.x; - let delta = mouse_x - bounds.x0 - offset; - port.pan_by(Vec2::new((delta / scale_x).ceil(), 0f64)); - ctx.set_handled(); - } - _ => (), - } - ctx.request_paint(); - } - Event::MouseUp(_) => { - self.held = BarHeldState::None; - ctx.set_active(false); - - if !scrollbar_is_hovered { - self.hovered = BarHoveredState::None; - self.reset_scrollbar_fade(|d| ctx.request_timer(d), env); - } - - ctx.set_handled(); - } - _ => (), // other events are a noop - } - } else if scrollbar_is_hovered { - // if we're over a scrollbar but not dragging - match event { - Event::MouseMove(event) => { - let offset_pos = event.pos + scroll_offset; - if self.point_hits_vertical_bar(port, offset_pos, env) { - self.hovered = BarHoveredState::Vertical; - } else if self.point_hits_horizontal_bar(port, offset_pos, env) { - self.hovered = BarHoveredState::Horizontal; - } else { - } - - self.opacity = env.get(theme::SCROLLBAR_MAX_OPACITY); - self.timer_id = TimerToken::INVALID; // Cancel any fade out in progress - ctx.request_paint(); - ctx.set_handled(); - } - Event::MouseDown(event) => { - let pos = event.pos + scroll_offset; - - if self.point_hits_vertical_bar(port, pos, env) { - ctx.set_active(true); - self.held = BarHeldState::Vertical( - // The bounds must be non-empty, because the point hits the scrollbar. - pos.y - - self - .calc_vertical_bar_bounds(port, env) - .unwrap() - .y0, - ); - } else if self.point_hits_horizontal_bar(port, pos, env) { - ctx.set_active(true); - self.held = BarHeldState::Horizontal( - // The bounds must be non-empty, because the point hits the scrollbar. - pos.x - - self - .calc_horizontal_bar_bounds(port, env) - .unwrap() - .x0, - ); - } else { - } - - ctx.set_handled(); - } - // if the mouse was downed elsewhere, moved over a scroll bar and released: noop. - Event::MouseUp(_) => (), - _ => (), - } - } else if !ctx.is_hot() { - match event { - // Event::MouseMove(_) => { - // // if we have just stopped hovering - // if self.hovered.is_hovered() && !scrollbar_is_hovered { - // self.hovered = BarHoveredState::None; - // self.reset_scrollbar_fade(|d| ctx.request_timer(d), env); - // } - // } - Event::AnimFrame(_) => { - if let Some(start) = self.fade_start { - let _elapsed = start.elapsed().subsec_millis(); - let diff = 0.02; - self.opacity = env.get(theme::SCROLLBAR_MAX_OPACITY) - - diff * (start.elapsed().subsec_millis() as f64 / 20.0); - if self.opacity > 0.0 { - ctx.request_anim_frame(); - } else { - self.fade_start = None; - } - } - } - Event::Timer(id) if *id == self.timer_id => { - // Schedule scroll bars animation - self.timer_id = TimerToken::INVALID; - self.fade_start = Some(Instant::now()); - ctx.request_anim_frame(); - ctx.set_handled(); - } - _ => (), - } - } - } - - /// Applies mousewheel scrolling if the event has not already been handled - pub fn handle_scroll( - &mut self, - port: &mut ViewportNew, - ctx: &mut EventCtx, - event: &Event, - env: &Env, - ) { - if !ctx.is_handled() { - if let Event::Wheel(mouse) = event { - if port.pan_by(mouse.wheel_delta.round()) {} - ctx.request_paint(); - self.reset_scrollbar_fade(|d| ctx.request_timer(d), env); - ctx.set_handled(); - } - } - } - - /// Perform any necessary action prompted by a lifecycle event - /// - /// Make sure to call on every lifecycle event - pub fn lifecycle( - &mut self, - ctx: &mut LifeCycleCtx, - event: &LifeCycle, - env: &Env, - ) { - match event { - LifeCycle::HotChanged(_) => { - self.reset_scrollbar_fade(|d| ctx.request_timer(d), env); - } - LifeCycle::Size(_) => { - // Show the scrollbars any time our size changes - ctx.request_paint(); - self.reset_scrollbar_fade(|d| ctx.request_timer(d), env); - } - _ => (), - } - } -} - -pub struct LapceScrollNew { - clip: ClipBoxNew, - pub scroll_component: ScrollComponentNew, -} - -impl> LapceScrollNew { - /// Create a new scroll container. - /// - /// This method will allow scrolling in all directions if child's bounds - /// are larger than the viewport. Use [vertical](#method.vertical) and - /// [horizontal](#method.horizontal) methods to limit scrolling to a specific axis. - pub fn new(child: W) -> LapceScrollNew { - Self { - clip: ClipBoxNew::new(child), - scroll_component: ScrollComponentNew::new(), - } - } - - /// Restrict scrolling to the vertical axis while locking child width. - pub fn vertical(mut self) -> Self { - self.clip.set_constrain_vertical(false); - self.clip.set_constrain_horizontal(true); - self - } - - /// Restrict scrolling to the horizontal axis while locking child height. - pub fn horizontal(mut self) -> Self { - self.clip.set_constrain_vertical(true); - self.clip.set_constrain_horizontal(false); - self - } - - /// Returns a reference to the child widget. - pub fn child(&self) -> &W { - self.clip.child() - } - - /// Returns a mutable reference to the child widget. - pub fn child_mut(&mut self) -> &mut W { - self.clip.child_mut() - } - - /// Returns the size of the child widget. - pub fn child_size(&self) -> Size { - self.clip.content_size() - } - - pub fn set_child_size(&mut self, size: Size) { - self.clip.port.content_size = size; - } - - /// Returns the current scroll offset. - pub fn offset(&self) -> Vec2 { - self.clip.viewport_origin().to_vec2() - } - - /// Scroll `delta` units. - /// - /// Returns `true` if the scroll offset has changed. - pub fn scroll_by(&mut self, delta: Vec2) -> bool { - self.clip.pan_by(delta) - } - - pub fn force_scroll_to(&mut self, point: Point) { - self.clip.force_pan_to(point) - } - - pub fn scroll_to(&mut self, point: Point) -> bool { - self.clip.pan_to(point) - } - - /// Scroll the minimal distance to show the target rect. - /// - /// If the target region is larger than the viewport, we will display the - /// portion that fits, prioritizing the portion closest to the origin. - #[allow(unused_variables)] - pub fn scroll_to_visible(&mut self, region: Rect, env: &Env) -> bool { - self.clip.pan_to_visible(region) - } -} - -impl> Widget for LapceScrollNew { - fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) { - let scroll_component = &mut self.scroll_component; - self.clip.with_port(|port| { - scroll_component.event(port, ctx, event, env); - }); - if !ctx.is_handled() { - self.clip.event(ctx, event, data, env); - } - - self.clip.with_port(|port| { - scroll_component.handle_scroll(port, ctx, event, env); - }); - - match event { - Event::Command(cmd) if cmd.is(LAPCE_UI_COMMAND) => { - let command = cmd.get_unchecked(LAPCE_UI_COMMAND); - if let LapceUICommand::ResetFade = command { - scroll_component - .reset_scrollbar_fade(|d| ctx.request_timer(d), env); - } - } - _ => (), - } - } - - fn lifecycle( - &mut self, - ctx: &mut LifeCycleCtx, - event: &LifeCycle, - data: &T, - env: &Env, - ) { - self.scroll_component.lifecycle(ctx, event, env); - self.clip.lifecycle(ctx, event, data, env); - } - - fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) { - self.clip.update(ctx, old_data, data, env); - } - - fn layout( - &mut self, - ctx: &mut LayoutCtx, - bc: &BoxConstraints, - data: &T, - env: &Env, - ) -> Size { - bc.debug_check("Scroll"); - - let old_viewport = self.clip.port; - let child_size = self.clip.layout(ctx, bc, data, env); - - let self_size = bc.constrain(child_size); - // The new size might have made the current scroll offset invalid. This makes it valid - // again. - let _ = self.scroll_by(Vec2::ZERO); - if old_viewport != self.clip.port { - self.scroll_component - .reset_scrollbar_fade(|d| ctx.request_timer(d), env); - } - - self_size - } - - fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { - self.clip.paint(ctx, data, env); - self.scroll_component.draw_bars( - ctx, - &self.clip.viewport(), - env, - data.get_config(), - ); - } -} - -pub struct LapcePadding { - left: f64, - right: f64, - top: f64, - bottom: f64, - - child: WidgetPod, -} - -impl> LapcePadding { - pub fn new(insets: impl Into, child: W) -> Self { - let insets = insets.into(); - Self { - left: insets.x0, - right: insets.x1, - top: insets.y0, - bottom: insets.y1, - child: WidgetPod::new(child), - } - } - - pub fn child_size(&self) -> Size { - self.child.layout_rect().size() - } - - pub fn child(&self) -> &W { - self.child.widget() - } - - /// Returns a mutable reference to the child widget. - pub fn child_mut(&mut self) -> &mut W { - self.child.widget_mut() - } -} - -impl> Widget for LapcePadding { - fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) { - self.child.event(ctx, event, data, env) - } - - fn lifecycle( - &mut self, - ctx: &mut LifeCycleCtx, - event: &LifeCycle, - data: &T, - env: &Env, - ) { - self.child.lifecycle(ctx, event, data, env) - } - - fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, data: &T, env: &Env) { - self.child.update(ctx, data, env); - } - - fn layout( - &mut self, - ctx: &mut LayoutCtx, - bc: &BoxConstraints, - data: &T, - env: &Env, - ) -> Size { - bc.debug_check("Padding"); - - let hpad = self.left + self.right; - let vpad = self.top + self.bottom; - - let child_bc = bc.shrink((hpad, vpad)); - let size = self.child.layout(ctx, &child_bc, data, env); - let origin = Point::new(self.left, self.top); - self.child.set_origin(ctx, data, env, origin); - - let my_size = Size::new(size.width + hpad, size.height + vpad); - let my_insets = self.child.compute_parent_paint_insets(my_size); - ctx.set_paint_insets(my_insets); - my_size - } - - fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { - self.child.paint(ctx, data, env); - } -} - -pub struct LapceIdentityWrapper { - id: WidgetId, - inner: W, -} - -impl LapceIdentityWrapper { - /// Assign an identity to a widget. - pub fn wrap(inner: W, id: WidgetId) -> LapceIdentityWrapper { - Self { id, inner } - } - - pub fn inner(&self) -> &W { - &self.inner - } - - pub fn inner_mut(&mut self) -> &mut W { - &mut self.inner - } -} - -impl> Widget for LapceIdentityWrapper { - fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) { - self.inner.event(ctx, event, data, env); - } - - fn lifecycle( - &mut self, - ctx: &mut LifeCycleCtx, - event: &LifeCycle, - data: &T, - env: &Env, - ) { - self.inner.lifecycle(ctx, event, data, env) - } - - fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) { - self.inner.update(ctx, old_data, data, env); - } - - fn layout( - &mut self, - ctx: &mut LayoutCtx, - bc: &BoxConstraints, - data: &T, - env: &Env, - ) -> Size { - self.inner.layout(ctx, bc, data, env) - } - - fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { - self.inner.paint(ctx, data, env); - } - - fn id(&self) -> Option { - Some(self.id) - } -}