Skip to content

Commit

Permalink
Scroll behavior tweaks.
Browse files Browse the repository at this point in the history
- Add option to disable kinetic scrolling
- Automatically disable kinetic scrolling for non-mouse input sources (when we can detect them)
- Implement touchpad acceleration
- Fix touhcpad scrolling on Wayland where units are reported in pixels instead of wheel units
- Tweak bonk behavior

Closes #11.
  • Loading branch information
misson20000 committed Aug 4, 2024
1 parent 8bcfa8b commit de57d88
Show file tree
Hide file tree
Showing 7 changed files with 325 additions and 62 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ default = ["gtk"]
gtk = ["dep:gtk"]

[dependencies]
gtk = { version = "0.9.0", optional = true, package = "gtk4" }
gtk = { version = "0.9.0", optional = true, package = "gtk4", features = ["v4_8"] }
glib = "0.20.0"
futures = "0.3.30"
lazy_static = "1.5.0"
Expand Down
10 changes: 6 additions & 4 deletions src/view/breadcrumbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,22 @@ impl CharmBreadcrumbWidget {

pub fn list_item_factory(lw: listing::ListingWidget) -> gtk::SignalListItemFactory {
let slif = gtk::SignalListItemFactory::new();
slif.connect_setup(move |_, obj| {
slif.connect_setup(move |_, obj| catch_panic! {
let obj = obj.downcast_ref::<gtk::ListItem>().unwrap();
let lw = lw.clone();
catch_panic! {
obj.set_child(Some(&Self::new(lw)));
}
obj.set_child(Some(&Self::new(lw)));
obj.set_activatable(false);
});
slif.connect_bind(|_, obj| catch_panic! {
let obj = obj.downcast_ref::<gtk::ListItem>().unwrap();
obj.child().unwrap().downcast::<Self>().unwrap().bind(Some(obj.item().unwrap().downcast::<CharmBreadcrumb>().unwrap()));
});
slif.connect_unbind(|_, obj| catch_panic! {
let obj = obj.downcast_ref::<gtk::ListItem>().unwrap();
obj.child().unwrap().downcast::<Self>().unwrap().bind(None);
});
slif.connect_teardown(|_, obj| catch_panic! {
let obj = obj.downcast_ref::<gtk::ListItem>().unwrap();
obj.set_child(gtk::Widget::NONE);
});
slif
Expand Down
62 changes: 59 additions & 3 deletions src/view/charm.cmb
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,16 @@
(11,39,"GtkBox",None,7,None,None,None,None,None,None),
(11,40,"GtkLabel",None,39,None,None,None,None,None,None),
(11,41,"GtkSwitch","dark-mode",39,None,None,None,1,None,None),
(11,42,"GtkAdjustment","indentation-width",11,None,None,None,-1,None,None)
(11,42,"GtkAdjustment","indentation-width",11,None,None,None,-1,None,None),
(11,43,"GtkGrid",None,5,None,None,None,-1,None,None),
(11,44,"GtkLabel",None,43,None,None,None,None,None,None),
(11,45,"GtkLabel",None,43,None,None,None,None,None,None),
(11,46,"GtkSpinButton",None,43,None,None,None,None,None,None),
(11,47,"GtkSpinButton",None,43,None,None,None,1,None,None),
(11,48,"GtkAdjustment","scroll-wheel-impulse",46,None,None,None,-1,None,None),
(11,49,"GtkAdjustment","scroll-deceleration",47,None,None,None,-1,None,None),
(11,50,"GtkLabel",None,43,None,None,None,None,None,None),
(11,51,"GtkSwitch","scroll-enable-kinetic",43,None,None,None,2,None,None)
</object>
<object_property>
(2,20,"GtkGrid","column-spacing","30",None,None,None,None,None,None,None,None,None),
Expand Down Expand Up @@ -411,6 +420,7 @@
(11,3,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None),
(11,4,"GtkStackPage","child",None,None,None,None,None,7,None,None,None,None),
(11,4,"GtkStackPage","title","Style",None,None,None,None,None,None,None,None,None),
(11,5,"GtkStackPage","child",None,None,None,None,None,43,None,None,None,None),
(11,5,"GtkStackPage","title","Scrolling",None,None,None,None,None,None,None,None,None),
(11,6,"GtkStackPage","title","Advanced",None,None,None,None,None,None,None,None,None),
(11,7,"GtkGrid","column-spacing","20",None,None,None,None,None,None,None,None,None),
Expand Down Expand Up @@ -459,7 +469,31 @@
(11,42,"GtkAdjustment","page-increment","1.0",None,None,None,None,None,None,None,None,None),
(11,42,"GtkAdjustment","step-increment","1.0",None,None,None,None,None,None,None,None,None),
(11,42,"GtkAdjustment","upper","16.0",None,None,None,None,None,None,None,None,None),
(11,42,"GtkAdjustment","value","2.0",None,None,None,None,None,None,None,None,None)
(11,42,"GtkAdjustment","value","2.0",None,None,None,None,None,None,None,None,None),
(11,43,"GtkGrid","column-spacing","20",None,None,None,None,None,None,None,None,None),
(11,43,"GtkGrid","row-spacing","5",None,None,None,None,None,None,None,None,None),
(11,43,"GtkWidget","halign","center",None,None,None,None,None,None,None,None,None),
(11,43,"GtkWidget","margin-bottom","10",None,None,None,None,None,None,None,None,None),
(11,43,"GtkWidget","margin-end","10",None,None,None,None,None,None,None,None,None),
(11,43,"GtkWidget","margin-start","10",None,None,None,None,None,None,None,None,None),
(11,43,"GtkWidget","margin-top","10",None,None,None,None,None,None,None,None,None),
(11,43,"GtkWidget","valign","center",None,None,None,None,None,None,None,None,None),
(11,44,"GtkLabel","label","Wheel Impulse",None,None,None,None,None,None,None,None,None),
(11,45,"GtkLabel","label","Deceleration",None,None,None,None,None,None,None,None,None),
(11,46,"GtkSpinButton","adjustment",None,None,None,None,None,48,None,None,None,None),
(11,46,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(11,47,"GtkSpinButton","adjustment",None,None,None,None,None,49,None,None,None,None),
(11,47,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None),
(11,48,"GtkAdjustment","page-size","10.0",None,None,None,None,None,None,None,None,None),
(11,48,"GtkAdjustment","step-increment","1.0",None,None,None,None,None,None,None,None,None),
(11,48,"GtkAdjustment","upper","400.0",None,None,None,None,None,None,None,None,None),
(11,48,"GtkAdjustment","value","60.0",None,None,None,None,None,None,None,None,None),
(11,49,"GtkAdjustment","page-size","100.0",None,None,None,None,None,None,None,None,None),
(11,49,"GtkAdjustment","step-increment","10.0",None,None,None,None,None,None,None,None,None),
(11,49,"GtkAdjustment","upper","1000.0",None,None,None,None,None,None,None,None,None),
(11,49,"GtkAdjustment","value","400.0",None,None,None,None,None,None,None,None,None),
(11,50,"GtkLabel","label","Kinetic Scrolling",None,None,None,None,None,None,None,None,None),
(11,51,"GtkWidget","halign","start",None,None,None,None,None,None,None,None,None)
</object_property>
<object_layout_property>
(2,20,21,"GtkGridLayoutChild","column","0",None,None,None,None),
Expand Down Expand Up @@ -703,7 +737,29 @@
(11,7,39,"GtkGridLayoutChild","column","0",None,None,None,None),
(11,7,39,"GtkGridLayoutChild","column-span","3",None,None,None,None),
(11,7,39,"GtkGridLayoutChild","row","2",None,None,None,None),
(11,7,39,"GtkGridLayoutChild","row-span","1",None,None,None,None)
(11,7,39,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(11,43,44,"GtkGridLayoutChild","column","0",None,None,None,None),
(11,43,44,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(11,43,44,"GtkGridLayoutChild","row","1",None,None,None,None),
(11,43,44,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(11,43,45,"GtkGridLayoutChild","column","0",None,None,None,None),
(11,43,45,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(11,43,45,"GtkGridLayoutChild","row","2",None,None,None,None),
(11,43,45,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(11,43,46,"GtkGridLayoutChild","column","1",None,None,None,None),
(11,43,46,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(11,43,46,"GtkGridLayoutChild","row","1",None,None,None,None),
(11,43,46,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(11,43,47,"GtkGridLayoutChild","column","1",None,None,None,None),
(11,43,47,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(11,43,47,"GtkGridLayoutChild","row","2",None,None,None,None),
(11,43,47,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(11,43,50,"GtkGridLayoutChild","column","0",None,None,None,None),
(11,43,50,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(11,43,50,"GtkGridLayoutChild","row-span","1",None,None,None,None),
(11,43,51,"GtkGridLayoutChild","column","1",None,None,None,None),
(11,43,51,"GtkGridLayoutChild","column-span","1",None,None,None,None),
(11,43,51,"GtkGridLayoutChild","row-span","1",None,None,None,None)
</object_layout_property>
<object_signal>
(2,8,6,"GtkButton","clicked","handle_save_as",None,None,1,None,None)
Expand Down
28 changes: 27 additions & 1 deletion src/view/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,16 @@ declare_config![Config {
file_access_delay: u64 = 0, /* milliseconds */

lookahead: usize = 20, /* lines */

#[bind("scroll-enable-kinetic")]
scroll_enable_kinetic: bool = true,

#[bind("scroll-wheel-impulse")]
scroll_wheel_impulse: f64 = 60.0, /* lines/second */

#[bind("scroll-deceleration")]
scroll_deceleration: f64 = 400.0, /* lines/second^2 */

scroll_spring: f64 = 240.0, /* 1/second^2 */
scroll_spring_damping: f64 = 17.0, /* viscous damping coefficient */

Expand Down Expand Up @@ -560,7 +567,8 @@ impl Item for bool {
}

fn update_binding(&self, binding: &Self::Binding) {
binding.set_state(*self)
binding.set_state(*self);
binding.set_active(*self);
}
}

Expand All @@ -581,3 +589,21 @@ impl Item for f32 {
binding.set_value(*self as f64)
}
}

impl Item for f64 {
type Binding = gtk::Adjustment;

fn bind<F: Fn(Self) + Clone + 'static>(builder: &gtk::Builder, id: &str, changer: F) -> Self::Binding {
let adj = builder.object::<gtk::Adjustment>(id).unwrap();

adj.connect_value_changed(move |adj| {
changer(adj.value());
});

adj
}

fn update_binding(&self, binding: &Self::Binding) {
binding.set_value(*self)
}
}
54 changes: 46 additions & 8 deletions src/view/listing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,12 @@ impl WidgetImpl for ListingWidgetImp {
snapshot.translate(&graphene::Point::new(0.0, helpers::pango_unscale(render.metrics.height())));
}
snapshot.restore();

snapshot.pop();
}
}
}

impl ListingWidgetImp {
fn init(&self, interior: sync::Arc<parking_lot::RwLock<Interior>>) {
if self.interior.set(interior).is_err() {
Expand Down Expand Up @@ -374,11 +374,24 @@ impl ListingWidget {
self.add_controller(ec_key);

/* Register scroll event controller */
let ec_scroll = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
ec_scroll.connect_scroll(clone!(#[weak(rename_to=lw)] self, #[upgrade_or] glib::Propagation::Proceed, move |_ecs, dx, dy| catch_panic! {
let ec_scroll = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL | gtk::EventControllerScrollFlags::KINETIC);
ec_scroll.connect_scroll(clone!(#[weak(rename_to=lw)] self, #[upgrade_or] glib::Propagation::Proceed, move |ecs, dx, dy| catch_panic! {
@default(glib::Propagation::Proceed);

let source = ecs.current_event_device().map(|device| device.source());

let kinetic_allowed = match source {
/* scroll wheels are allowed to use kinetic scrolling */
Some(gdk::InputSource::Mouse) => true,

/* trackpads, trackpoints, touch screens, etc. should never use kinetic acceleration */
_ => false,
};

lw.imp().interior.get().unwrap().write().scroll(&lw, dx, dy)
lw.imp().interior.get().unwrap().write().scroll(&lw, ecs.unit(), dx, dy, kinetic_allowed)
}));
ec_scroll.connect_decelerate(clone!(#[weak(rename_to=lw)] self, move |ecs, vx, vy| catch_panic! {
lw.imp().interior.get().unwrap().write().scroll_decelerate(&lw, ecs.unit(), vx, vy);
}));
self.add_controller(ec_scroll);

Expand Down Expand Up @@ -638,7 +651,7 @@ impl Interior {
//let line_count = height.div_ceil(line_height) as usize;
let line_count = (((height * pango::SCALE) + line_height - 1) / line_height) as usize;

self.window.resize(line_count + 2 + (2 * self.scroll.get_lookahead()));
self.window.resize(line_count + (2 * self.scroll.get_lookahead()));
self.update_breadcrumbs();
self.request_work();
}
Expand Down Expand Up @@ -766,12 +779,18 @@ impl Interior {
r
}

fn scroll(&mut self, widget: &ListingWidget, _dx: f64, dy: f64) -> glib::Propagation {
fn scroll(&mut self, widget: &ListingWidget, unit: gdk::ScrollUnit, _dx: f64, dy: f64, kinetic_allowed: bool) -> glib::Propagation {
let _circumstances = crashreport::circumstances([
crashreport::Circumstance::InWindow(self.charm_window_id),
]);

self.scroll.scroll_wheel_impulse(dy);
let divisor = match unit {
gdk::ScrollUnit::Wheel => 1.0,
gdk::ScrollUnit::Surface => helpers::pango_unscale(self.render.metrics.height()) as f64,
_ => 1.0,
};

self.scroll.scroll_wheel_impulse(dy / divisor, kinetic_allowed);

self.collect_events(widget);

Expand All @@ -780,6 +799,25 @@ impl Interior {
glib::Propagation::Stop
}

fn scroll_decelerate(&mut self, widget: &ListingWidget, unit: gdk::ScrollUnit, _vx: f64, vy: f64) {
let _circumstances = crashreport::circumstances([
crashreport::Circumstance::InWindow(self.charm_window_id),
]);

let divisor = match unit {
gdk::ScrollUnit::Wheel => 1.0,
gdk::ScrollUnit::Surface => helpers::pango_unscale(self.render.metrics.height()) as f64,
_ => 1.0,
};

/* gtk reports velocity in pixels/ms, we expect "units"/second */
self.scroll.scroll_decelerate(vy / divisor / 1000.0);

self.collect_events(widget);

self.update_breadcrumbs();
}

fn update_breadcrumbs(&self) {
let mut tok = None;

Expand Down
Loading

0 comments on commit de57d88

Please sign in to comment.