Skip to content

Commit 9f85e0c

Browse files
committed
Reworked Scrollable to account for lack of widget order guarantees.
Fixed thumb "snapping" bug on scrollable when cursor is out of bounds.
1 parent d91f4f6 commit 9f85e0c

9 files changed

Lines changed: 618 additions & 691 deletions

File tree

core/src/point.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,11 @@ impl std::ops::Sub<Point> for Point {
7575
Vector::new(self.x - point.x, self.y - point.y)
7676
}
7777
}
78+
79+
impl std::ops::Add<Point> for Point {
80+
type Output = Point;
81+
82+
fn add(self, point: Point) -> Point {
83+
Point::new(self.x + point.x, self.y + point.y)
84+
}
85+
}

core/src/rectangle.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ impl Rectangle<f32> {
111111
}
112112
}
113113

114+
impl std::cmp::PartialOrd for Rectangle<f32> {
115+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
116+
(self.width * self.height).partial_cmp(&(other.width * other.height))
117+
}
118+
}
119+
114120
impl std::ops::Mul<f32> for Rectangle<f32> {
115121
type Output = Self;
116122

examples/scrollable/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ publish = false
77

88
[dependencies]
99
iced = { path = "../..", features = ["debug"] }
10-
lazy_static = "1.4"
10+
once_cell = "1.16.0"

examples/scrollable/src/main.rs

Lines changed: 54 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
use iced::widget::scrollable::{Scrollbar, Scroller};
1+
use iced::widget::scrollable::{Properties, Scrollbar, Scroller};
22
use iced::widget::{
33
button, column, container, horizontal_space, progress_bar, radio, row,
44
scrollable, slider, text, vertical_space,
55
};
6-
use iced::{executor, theme, Alignment, Color, Vector};
6+
use iced::{executor, theme, Alignment, Color, Point};
77
use iced::{Application, Command, Element, Length, Settings, Theme};
8-
use lazy_static::lazy_static;
8+
use once_cell::sync::Lazy;
99

10-
lazy_static! {
11-
static ref SCROLLABLE_ID: scrollable::Id = scrollable::Id::unique();
12-
}
10+
static SCROLLABLE_ID: Lazy<scrollable::Id> = Lazy::new(scrollable::Id::unique);
1311

1412
pub fn main() -> iced::Result {
1513
ScrollableDemo::run(Settings::default())
@@ -20,7 +18,7 @@ struct ScrollableDemo {
2018
scrollbar_width: u16,
2119
scrollbar_margin: u16,
2220
scroller_width: u16,
23-
current_scroll_offset: Vector<f32>,
21+
current_scroll_offset: Point,
2422
}
2523

2624
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
@@ -36,9 +34,9 @@ enum Message {
3634
ScrollbarWidthChanged(u16),
3735
ScrollbarMarginChanged(u16),
3836
ScrollerWidthChanged(u16),
39-
ScrollToBeginning(scrollable::Direction),
40-
ScrollToEnd(scrollable::Direction),
41-
Scrolled(Vector<f32>),
37+
ScrollToBeginning,
38+
ScrollToEnd,
39+
Scrolled(Point),
4240
}
4341

4442
impl Application for ScrollableDemo {
@@ -54,7 +52,7 @@ impl Application for ScrollableDemo {
5452
scrollbar_width: 10,
5553
scrollbar_margin: 0,
5654
scroller_width: 10,
57-
current_scroll_offset: Vector::new(0.0, 0.0),
55+
current_scroll_offset: Point::ORIGIN,
5856
},
5957
Command::none(),
6058
)
@@ -67,10 +65,13 @@ impl Application for ScrollableDemo {
6765
fn update(&mut self, message: Message) -> Command<Message> {
6866
match message {
6967
Message::SwitchDirection(direction) => {
70-
self.current_scroll_offset = Vector::new(0.0, 0.0);
68+
self.current_scroll_offset = Point::ORIGIN;
7169
self.scrollable_direction = direction;
7270

73-
Command::none()
71+
scrollable::snap_to(
72+
SCROLLABLE_ID.clone(),
73+
self.current_scroll_offset,
74+
)
7475
}
7576
Message::ScrollbarWidthChanged(width) => {
7677
self.scrollbar_width = width;
@@ -87,40 +88,20 @@ impl Application for ScrollableDemo {
8788

8889
Command::none()
8990
}
90-
Message::ScrollToBeginning(direction) => {
91-
match direction {
92-
scrollable::Direction::Horizontal => {
93-
self.current_scroll_offset.x = 0.0;
94-
}
95-
scrollable::Direction::Vertical => {
96-
self.current_scroll_offset.y = 0.0;
97-
}
98-
}
91+
Message::ScrollToBeginning => {
92+
self.current_scroll_offset = Point::ORIGIN;
9993

10094
scrollable::snap_to(
10195
SCROLLABLE_ID.clone(),
102-
Vector::new(
103-
self.current_scroll_offset.x,
104-
self.current_scroll_offset.y,
105-
),
96+
self.current_scroll_offset,
10697
)
10798
}
108-
Message::ScrollToEnd(direction) => {
109-
match direction {
110-
scrollable::Direction::Horizontal => {
111-
self.current_scroll_offset.x = 1.0;
112-
}
113-
scrollable::Direction::Vertical => {
114-
self.current_scroll_offset.y = 1.0;
115-
}
116-
}
99+
Message::ScrollToEnd => {
100+
self.current_scroll_offset = Point::new(1.0, 1.0);
117101

118102
scrollable::snap_to(
119103
SCROLLABLE_ID.clone(),
120-
Vector::new(
121-
self.current_scroll_offset.x,
122-
self.current_scroll_offset.y,
123-
),
104+
self.current_scroll_offset,
124105
)
125106
}
126107
Message::Scrolled(offset) => {
@@ -186,56 +167,53 @@ impl Application for ScrollableDemo {
186167
.spacing(20)
187168
.width(Length::Fill);
188169

189-
let scroll_to_end_button = |direction: scrollable::Direction| {
170+
let scroll_to_end_button = || {
190171
button("Scroll to end")
191172
.padding(10)
192-
.width(Length::Units(120))
193-
.on_press(Message::ScrollToEnd(direction))
173+
.on_press(Message::ScrollToEnd)
194174
};
195175

196-
let scroll_to_beginning_button = |direction: scrollable::Direction| {
176+
let scroll_to_beginning_button = || {
197177
button("Scroll to beginning")
198178
.padding(10)
199-
.width(Length::Units(120))
200-
.on_press(Message::ScrollToBeginning(direction))
179+
.on_press(Message::ScrollToBeginning)
201180
};
202181

203182
let scrollable_content: Element<Message> =
204183
Element::from(match self.scrollable_direction {
205184
Direction::Vertical => scrollable(
206185
column![
207-
scroll_to_end_button(scrollable::Direction::Vertical),
186+
scroll_to_end_button(),
208187
text("Beginning!"),
209188
vertical_space(Length::Units(1200)),
210189
text("Middle!"),
211190
vertical_space(Length::Units(1200)),
212191
text("End!"),
213-
scroll_to_beginning_button(
214-
scrollable::Direction::Vertical
215-
),
192+
scroll_to_beginning_button(),
216193
]
217194
.width(Length::Fill)
218195
.align_items(Alignment::Center)
219196
.padding([40, 0, 40, 0])
220197
.spacing(40),
221198
)
222199
.height(Length::Fill)
223-
.scrollbar_width(self.scrollbar_width)
224-
.scrollbar_margin(self.scrollbar_margin)
225-
.scroller_width(self.scroller_width)
200+
.vertical_scroll(
201+
Properties::new()
202+
.width(self.scrollbar_width)
203+
.margin(self.scrollbar_margin)
204+
.scroller_width(self.scroller_width),
205+
)
226206
.id(SCROLLABLE_ID.clone())
227207
.on_scroll(Message::Scrolled),
228208
Direction::Horizontal => scrollable(
229209
row![
230-
scroll_to_end_button(scrollable::Direction::Horizontal),
210+
scroll_to_end_button(),
231211
text("Beginning!"),
232212
horizontal_space(Length::Units(1200)),
233213
text("Middle!"),
234214
horizontal_space(Length::Units(1200)),
235215
text("End!"),
236-
scroll_to_beginning_button(
237-
scrollable::Direction::Horizontal
238-
),
216+
scroll_to_beginning_button(),
239217
]
240218
.height(Length::Units(450))
241219
.align_items(Alignment::Center)
@@ -244,14 +222,12 @@ impl Application for ScrollableDemo {
244222
)
245223
.height(Length::Fill)
246224
.horizontal_scroll(
247-
scrollable::Horizontal::new()
248-
.scrollbar_height(self.scrollbar_width)
249-
.scrollbar_margin(self.scrollbar_margin)
250-
.scroller_height(self.scroller_width),
225+
Properties::new()
226+
.width(self.scrollbar_width)
227+
.margin(self.scrollbar_margin)
228+
.scroller_width(self.scroller_width),
251229
)
252-
.style(theme::Scrollable::Custom(Box::new(
253-
ScrollbarCustomStyle,
254-
)))
230+
.style(theme::Scrollable::custom(ScrollbarCustomStyle))
255231
.id(SCROLLABLE_ID.clone())
256232
.on_scroll(Message::Scrolled),
257233
Direction::Multi => scrollable(
@@ -261,45 +237,43 @@ impl Application for ScrollableDemo {
261237
text("Let's do some scrolling!"),
262238
vertical_space(Length::Units(2400))
263239
],
264-
scroll_to_end_button(scrollable::Direction::Horizontal),
240+
scroll_to_end_button(),
265241
text("Horizontal - Beginning!"),
266242
horizontal_space(Length::Units(1200)),
267243
//vertical content
268244
column![
269245
text("Horizontal - Middle!"),
270-
scroll_to_end_button(
271-
scrollable::Direction::Vertical
272-
),
246+
scroll_to_end_button(),
273247
text("Vertical - Beginning!"),
274248
vertical_space(Length::Units(1200)),
275249
text("Vertical - Middle!"),
276250
vertical_space(Length::Units(1200)),
277251
text("Vertical - End!"),
278-
scroll_to_beginning_button(
279-
scrollable::Direction::Vertical
280-
)
252+
scroll_to_beginning_button(),
253+
vertical_space(Length::Units(40)),
281254
]
282255
.align_items(Alignment::Fill)
283256
.spacing(40),
284257
horizontal_space(Length::Units(1200)),
285258
text("Horizontal - End!"),
286-
scroll_to_beginning_button(
287-
scrollable::Direction::Horizontal
288-
),
259+
scroll_to_beginning_button(),
289260
]
290261
.align_items(Alignment::Center)
291262
.padding([0, 40, 0, 40])
292263
.spacing(40),
293264
)
294265
.height(Length::Fill)
295-
.scrollbar_width(self.scrollbar_width)
296-
.scrollbar_margin(self.scrollbar_margin)
297-
.scroller_width(self.scroller_width)
266+
.vertical_scroll(
267+
Properties::new()
268+
.width(self.scrollbar_width)
269+
.margin(self.scrollbar_margin)
270+
.scroller_width(self.scroller_width),
271+
)
298272
.horizontal_scroll(
299-
scrollable::Horizontal::new()
300-
.scrollbar_height(self.scrollbar_width)
301-
.scrollbar_margin(self.scrollbar_margin)
302-
.scroller_height(self.scroller_width),
273+
Properties::new()
274+
.width(self.scrollbar_width)
275+
.margin(self.scrollbar_margin)
276+
.scroller_width(self.scroller_width),
303277
)
304278
.style(theme::Scrollable::Custom(Box::new(
305279
ScrollbarCustomStyle,

examples/websocket/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use iced::alignment::{self, Alignment};
44
use iced::widget::{
55
button, column, container, row, scrollable, text, text_input, Column,
66
};
7-
use iced::{executor, Vector};
7+
use iced::{executor, Point};
88
use iced::{
99
Application, Color, Command, Element, Length, Settings, Subscription, Theme,
1010
};
@@ -83,7 +83,7 @@ impl Application for WebSocket {
8383

8484
scrollable::snap_to(
8585
MESSAGE_LOG.clone(),
86-
Vector::new(0.0, 1.0),
86+
Point::new(0.0, 1.0),
8787
)
8888
}
8989
},

native/src/widget/operation/scrollable.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
//! Operate on widgets that can be scrolled.
22
use crate::widget::{Id, Operation};
3-
use iced_core::Vector;
3+
use iced_core::Point;
44

55
/// The internal state of a widget that can be scrolled.
66
pub trait Scrollable {
7-
/// Snaps the scroll of the widget to the given `percentage`.
8-
fn snap_to(&mut self, percentage: Vector<f32>);
7+
/// Snaps the scroll of the widget to the given `percentage` along the horizontal & vertical axis.
8+
fn snap_to(&mut self, percentage: Point);
99
}
1010

1111
/// Produces an [`Operation`] that snaps the widget with the given [`Id`] to
1212
/// the provided `percentage`.
13-
pub fn snap_to<T>(target: Id, percentage: Vector<f32>) -> impl Operation<T> {
13+
pub fn snap_to<T>(target: Id, percentage: Point) -> impl Operation<T> {
1414
struct SnapTo {
1515
target: Id,
16-
percentage: Vector<f32>,
16+
percentage: Point,
1717
}
1818

1919
impl<T> Operation<T> for SnapTo {

0 commit comments

Comments
 (0)