agg_gui/widgets/rich_text/editor/
scroll.rs1use crate::draw_ctx::DrawCtx;
11use crate::geometry::Point;
12use crate::widgets::scroll_view::{
13 current_scroll_style, current_scroll_visibility, ScrollBarStyle, ScrollBarVisibility,
14};
15use crate::widgets::scrollbar::{
16 paint_prepared_scrollbar, ScrollbarGeometry, ScrollbarOrientation, DEFAULT_GRAB_MARGIN,
17};
18
19use super::super::model::DocPos;
20use super::RichTextEdit;
21
22pub(crate) enum ScrollMove {
24 Dragging(bool),
26 Hover(bool),
28}
29
30impl RichTextEdit {
31 pub(crate) fn max_scroll_y(&self) -> f64 {
33 (self.content_height() - self.inner_height()).max(0.0)
34 }
35
36 pub fn scroll_offset(&self) -> f64 {
38 self.vbar.offset
39 }
40
41 fn scroll_style(&self) -> ScrollBarStyle {
42 current_scroll_style()
43 }
44 fn scroll_visibility(&self) -> ScrollBarVisibility {
45 current_scroll_visibility()
46 }
47
48 pub(crate) fn sync_scroll(&mut self) {
50 self.vbar.enabled = true;
51 self.vbar.content = self.content_height();
52 self.vbar.clamp_offset(self.inner_height());
53 }
54
55 fn v_scrollbar_geometry(&self) -> ScrollbarGeometry {
56 ScrollbarGeometry {
57 orientation: ScrollbarOrientation::Vertical,
58 track_start: self.padding,
59 track_end: (self.bounds.height - self.padding).max(self.padding),
60 cross_end: (self.bounds.width - 2.0).max(0.0),
61 hit_margin: DEFAULT_GRAB_MARGIN,
62 }
63 }
64
65 pub(crate) fn ensure_caret_visible(&mut self) {
67 let caret = self.core.borrow().caret();
68 let vlines = self.visual_lines();
69 let vi = self.caret_visual_line(caret);
70 let Some(&(_, _, y_top, height)) = vlines.get(vi) else {
71 return;
72 };
73 let inner_h = self.inner_height();
74 let top = y_top;
75 let bottom = y_top + height;
76 let mut off = self.vbar.offset;
77 if top < off {
78 off = top;
79 } else if bottom > off + inner_h {
80 off = bottom - inner_h;
81 }
82 self.vbar.offset = off.clamp(0.0, self.max_scroll_y());
83 }
84
85 pub(crate) fn scroll_by_wheel(&mut self, delta_y: f64) -> bool {
86 if self.max_scroll_y() <= 0.0 {
87 return false;
88 }
89 let line_h = self.wheel_line_height();
96 self.vbar
97 .scroll_by(-delta_y * 3.0 * line_h, self.inner_height())
98 }
99
100 fn wheel_line_height(&self) -> f64 {
104 self.visual_lines()
105 .first()
106 .map(|&(_, _, _, h)| h)
107 .filter(|h| *h > 0.0)
108 .unwrap_or(16.0 * 1.35)
109 }
110
111 pub(crate) fn scrollbar_begin_drag(&mut self, pos: Point) -> bool {
112 if self.max_scroll_y() <= 0.0 {
113 return false;
114 }
115 let geom = self.v_scrollbar_geometry();
116 self.vbar
117 .begin_drag(pos, self.inner_height(), self.scroll_style(), geom)
118 }
119
120 pub(crate) fn scrollbar_on_mouse_move(&mut self, pos: Point) -> ScrollMove {
121 let geom = self.v_scrollbar_geometry();
122 let vp = self.inner_height();
123 let style = self.scroll_style();
124 if self.vbar.dragging {
125 ScrollMove::Dragging(self.vbar.drag_to(pos, vp, style, geom))
126 } else {
127 ScrollMove::Hover(self.vbar.update_hover(pos, vp, style, geom))
128 }
129 }
130
131 pub(crate) fn scrollbar_end_drag(&mut self) -> bool {
132 let was = self.vbar.dragging;
133 self.vbar.dragging = false;
134 was
135 }
136
137 pub(crate) fn paint_scrollbar(&mut self, ctx: &mut dyn DrawCtx) {
138 let vp = self.inner_height();
139 let style = self.scroll_style();
140 let vis = self.scroll_visibility();
141 let geom = self.v_scrollbar_geometry();
142 if let Some(bar) = self.vbar.prepare_paint(vp, style, vis, geom) {
143 paint_prepared_scrollbar(ctx, bar);
144 }
145 }
146
147 pub(crate) fn scrollbar_animating(&self) -> bool {
148 self.vbar.animation_active()
149 }
150
151 pub(crate) fn ensure_pos_visible(&mut self, _caret: DocPos) {
154 self.ensure_caret_visible();
155 }
156}