diff --git a/src/backend/manager.rs b/src/backend/manager.rs index 02c8a31..fa9b52a 100644 --- a/src/backend/manager.rs +++ b/src/backend/manager.rs @@ -14,10 +14,10 @@ use wp::{ }; use std::{str::FromStr, cell::RefCell}; use crate::{ - backend::pwnodeobject::PwNodeObject, + backend::PwNodeObject, ui::PwvucontrolWindowView, - backend::pwnodemodel::PwNodeModel, - backend::pwdeviceobject::PwDeviceObject, + backend::PwNodeModel, + backend::PwDeviceObject, ui::PwvucontrolWindow, PwvucontrolApplication, backend::NodeType, diff --git a/src/backend/pwchannelobject.rs b/src/backend/pwchannelobject.rs index ca39100..f95e314 100644 --- a/src/backend/pwchannelobject.rs +++ b/src/backend/pwchannelobject.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use crate::backend::pwnodeobject::PwNodeObject; +use crate::{backend::PwNodeObject, macros::*}; use std::cell::{Cell, RefCell}; use gtk::{ glib::{self, Properties, Value}, @@ -8,7 +8,6 @@ use gtk::{ subclass::prelude::* }; use wireplumber as wp; -use crate::macros::*; mod imp { use super::*; diff --git a/src/backend/pwdeviceobject.rs b/src/backend/pwdeviceobject.rs index 618bee2..8c57207 100644 --- a/src/backend/pwdeviceobject.rs +++ b/src/backend/pwdeviceobject.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use crate::backend::pwprofileobject::PwProfileObject; +use crate::backend::PwProfileObject; use glib::{ self, clone, subclass::{prelude::*, Signal}, diff --git a/src/backend/pwnodemodel.rs b/src/backend/pwnodemodel.rs index 167ae06..92957a7 100644 --- a/src/backend/pwnodemodel.rs +++ b/src/backend/pwnodemodel.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use crate::backend::pwnodeobject::PwNodeObject; +use crate::backend::PwNodeObject; use gtk::{gio, glib, prelude::*, subclass::prelude::*}; use std::cell::RefCell; use im_rc::Vector; diff --git a/src/ui/devicebox.rs b/src/ui/devicebox.rs index 51dc626..64c0154 100644 --- a/src/ui/devicebox.rs +++ b/src/ui/devicebox.rs @@ -1,11 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use crate::backend::PwDeviceObject; -use crate::ui::PwProfileDropDown; +use crate::{backend::PwDeviceObject, ui::PwProfileDropDown}; use gtk::{prelude::*, subclass::prelude::*}; use std::cell::RefCell; - mod imp { use super::*; diff --git a/src/ui/levelprovider.rs b/src/ui/levelprovider.rs index 8098c60..088058c 100644 --- a/src/ui/levelprovider.rs +++ b/src/ui/levelprovider.rs @@ -5,7 +5,6 @@ use std::{fmt::Debug, time::Duration}; use glib::{self, clone, ControlFlow}; use pipewire::{properties, spa, stream::*, Context, Loop}; use std::os::fd::AsRawFd; - use crate::ui::PwVolumeBox; pub struct LevelbarProvider { diff --git a/src/ui/outputbox.rs b/src/ui/outputbox.rs index d5ae575..8bbb080 100644 --- a/src/ui/outputbox.rs +++ b/src/ui/outputbox.rs @@ -1,16 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-or-later use crate::{ - backend::PwvucontrolManager, - backend::PwNodeObject, - ui::PwVolumeBox, - ui::PwVolumeBoxImpl, - ui::PwOutputDropDown, + backend::{PwNodeObject, PwvucontrolManager}, + macros::*, + ui::{PwOutputDropDown, PwVolumeBox, PwVolumeBoxImpl}, }; -use glib::{closure_local, clone}; +use glib::{clone, closure_local}; use gtk::{prelude::*, subclass::prelude::*}; use wireplumber as wp; -use crate::macros::*; use super::volumebox::PwVolumeBoxExt; @@ -23,25 +20,24 @@ mod imp { #[template_child] pub output_dropdown: TemplateChild, } - + #[glib::object_subclass] impl ObjectSubclass for PwOutputBox { const NAME: &'static str = "PwOutputBox"; type Type = super::PwOutputBox; type ParentType = PwVolumeBox; - + fn class_init(klass: &mut Self::Class) { klass.bind_template(); } - + fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } - + impl ObjectImpl for PwOutputBox { fn constructed(&self) { - let manager = PwvucontrolManager::default(); let obj = self.obj(); @@ -53,7 +49,6 @@ mod imp { self.parent_constructed(); - if let Some(metadata) = manager.imp().metadata.borrow().as_ref() { let boundid = item.boundid(); let widget = self.obj(); @@ -75,15 +70,12 @@ mod imp { widget.obj().update_output_device_dropdown(); })); } - } impl WidgetImpl for PwOutputBox {} impl ListBoxRowImpl for PwOutputBox {} impl PwVolumeBoxImpl for PwOutputBox {} - - impl PwOutputBox { - } + impl PwOutputBox {} } glib::wrapper! { @@ -94,9 +86,7 @@ glib::wrapper! { impl PwOutputBox { pub(crate) fn new(node_object: &impl glib::IsA) -> Self { - glib::Object::builder() - .property("node-object", node_object) - .build() + glib::Object::builder().property("node-object", node_object).build() } pub(crate) fn update_output_device_dropdown(&self) { @@ -141,7 +131,7 @@ impl PwOutputBox { deftarget.boundid(), deftarget.serial() ); - output_dropdown.set_selected_no_send(pos+1); + output_dropdown.set_selected_no_send(pos + 1); } } else { output_dropdown.set_selected_no_send(0); @@ -163,5 +153,4 @@ impl PwOutputBox { // } } } - } diff --git a/src/ui/profile_dropdown.rs b/src/ui/profile_dropdown.rs index d05890d..48d840e 100644 --- a/src/ui/profile_dropdown.rs +++ b/src/ui/profile_dropdown.rs @@ -1,17 +1,17 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use crate::backend::PwDeviceObject; +use crate::{ + backend::{PwDeviceObject, PwProfileObject}, + macros::*, + ui::PwProfileRow, +}; +use glib::clone; use glib::closure_local; use gtk::{self, prelude::*, subclass::prelude::*}; -use glib::clone; -use wp::pw::ProxyExt; use std::cell::{Cell, RefCell}; use wireplumber as wp; -use crate::ui::PwProfileRow; -use crate::macros::*; +use wp::pw::ProxyExt; mod imp { - use crate::backend::PwProfileObject; - use super::*; #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)] @@ -25,7 +25,6 @@ mod imp { pub profile_dropdown: TemplateChild, pub(super) block_signal: Cell, - //pub(super) stringlist: RefCell, } @@ -114,26 +113,27 @@ mod imp { self.block_signal.set(false); - deviceobject.connect_local("pre-update-profile", false, + deviceobject.connect_local( + "pre-update-profile", + false, clone!(@weak self as widget => @default-return None, move |_| { widget.block_signal.set(true); None - }) + }), ); - deviceobject.connect_local("post-update-profile", false, - clone!(@weak self as widget => @default-return None, move |_| { + deviceobject.connect_local( + "post-update-profile", + false, + clone!(@weak self as widget => @default-return None, move |_| { widget.block_signal.set(false); widget.update_selected(); None - }) + }), ); - - deviceobject.connect_profile_index_notify( - clone!(@weak self as widget => move |_| widget.update_selected()) - ); + deviceobject.connect_profile_index_notify(clone!(@weak self as widget => move |_| widget.update_selected())); } else { self.profile_dropdown.set_model(gtk::gio::ListModel::NONE); } @@ -159,10 +159,7 @@ mod imp { fn bind_handler(item: &glib::Object, dropdown: >k::DropDown) { let item: >k::ListItem = item.downcast_ref().expect("ListItem"); - let profilerow = item - .child() - .and_downcast::() - .expect("PwProfileRow child"); + let profilerow = item.child().and_downcast::().expect("PwProfileRow child"); let signal = dropdown.connect_selected_item_notify(clone!(@weak item => move |dropdown| { let profilerow = item @@ -208,7 +205,7 @@ mod imp { if let Some(deviceobject) = widget.deviceobject() { pwvucontrol_critical!("Had set profile to {}", dropdown.selected()); - + deviceobject.set_profile(dropdown.selected() as i32); } }); diff --git a/src/ui/profilerow.rs b/src/ui/profilerow.rs index e0e4607..a06dacd 100644 --- a/src/ui/profilerow.rs +++ b/src/ui/profilerow.rs @@ -3,7 +3,6 @@ use glib::closure_local; use gtk::{prelude::*, subclass::prelude::*}; use std::cell::RefCell; - use crate::backend::ParamAvailability; mod imp { diff --git a/src/ui/route_dropdown.rs b/src/ui/route_dropdown.rs index a0d15c9..f482283 100644 --- a/src/ui/route_dropdown.rs +++ b/src/ui/route_dropdown.rs @@ -1,17 +1,18 @@ // SPDX-License-Identifier: GPL-3.0-or-later +use crate::{ + backend::{NodeType, PwNodeObject, PwRouteFilterModel, PwRouteObject}, + macros::*, + ui::PwProfileRow, +}; +use glib::clone; use glib::closure_local; use gtk::{self, prelude::*, subclass::prelude::*}; -use glib::clone; -use wp::pw::ProxyExt; use std::cell::{Cell, RefCell}; use wireplumber as wp; -use crate::ui::PwProfileRow; -use crate::macros::*; +use wp::pw::ProxyExt; mod imp { - use crate::backend::{NodeType, PwNodeObject, PwRouteFilterModel, PwRouteObject}; - use super::*; #[derive(Debug, Default, gtk::CompositeTemplate, glib::Properties)] @@ -60,7 +61,7 @@ mod imp { match nodeobject.nodetype() { NodeType::Source => Some(deviceobject.route_index_input()), NodeType::Sink => Some(deviceobject.route_index_output()), - _ => None + _ => None, } } @@ -72,7 +73,7 @@ mod imp { match nodeobject.nodetype() { NodeType::Source => Some(deviceobject.routemodel_input()), NodeType::Sink => Some(deviceobject.routemodel_output()), - _ => None + _ => None, } } @@ -80,7 +81,6 @@ mod imp { self.nodeobject.replace(new_nodeobject.cloned()); if let Some(nodeobject) = new_nodeobject { - let deviceobject = nodeobject.get_device().expect("device"); self.block_signal.set(true); @@ -93,27 +93,29 @@ mod imp { self.block_signal.set(false); - deviceobject.connect_local("pre-update-route", false, + deviceobject.connect_local( + "pre-update-route", + false, clone!(@weak self as widget => @default-return None, move |_| { widget.block_signal.set(true); None - }) + }), ); - deviceobject.connect_local("post-update-route", false, - clone!(@weak self as widget => @default-return None, move |_| { + deviceobject.connect_local( + "post-update-route", + false, + clone!(@weak self as widget => @default-return None, move |_| { widget.block_signal.set(false); pwvucontrol_info!("About to call widget.update_selected() inside post-update-route handler"); widget.update_selected(); None - }) + }), ); - deviceobject.connect_route_index_output_notify( - clone!(@weak self as widget => move |_| widget.update_selected()) - ); + deviceobject.connect_route_index_output_notify(clone!(@weak self as widget => move |_| widget.update_selected())); } else { self.route_dropdown.set_model(gtk::gio::ListModel::NONE); } @@ -139,10 +141,7 @@ mod imp { fn bind_handler(item: &glib::Object, dropdown: >k::DropDown) { let item: >k::ListItem = item.downcast_ref().expect("ListItem"); - let profilerow = item - .child() - .and_downcast::() - .expect("PwProfileRow child"); + let profilerow = item.child().and_downcast::().expect("PwProfileRow child"); let signal = dropdown.connect_selected_item_notify(clone!(@weak item => move |dropdown| { let profilerow = item @@ -176,7 +175,6 @@ mod imp { self.route_dropdown.set_factory(Some(&factory)); self.route_dropdown.set_list_factory(Some(&list_factory)); - let widget = self.obj(); let selected_handler = closure_local!( @watch widget => move |dropdown: >k::DropDown, _pspec: &glib::ParamSpec| { @@ -206,7 +204,6 @@ glib::wrapper! { } impl PwRouteDropDown { - pub fn set_selected_no_send(&self, position: u32) { let imp = self.imp(); diff --git a/src/ui/sinkbox.rs b/src/ui/sinkbox.rs index 7aaf163..baeb05a 100644 --- a/src/ui/sinkbox.rs +++ b/src/ui/sinkbox.rs @@ -1,20 +1,17 @@ // SPDX-License-Identifier: GPL-3.0-or-later +use super::volumebox::PwVolumeBoxExt; use crate::{ - backend::PwvucontrolManager, - backend::PwNodeObject, - ui::{PwVolumeBox, PwVolumeBoxImpl}, + backend::{NodeType, PwNodeObject, PwvucontrolManager}, + pwvucontrol_info, + ui::{PwRouteDropDown, PwVolumeBox, PwVolumeBoxImpl}, }; use glib::clone; use gtk::{prelude::*, subclass::prelude::*}; use std::cell::Cell; use wireplumber as wp; -use super::volumebox::PwVolumeBoxExt; -use crate::{backend::NodeType, pwvucontrol_info}; -use crate::ui::PwRouteDropDown; mod imp { - use super::*; #[derive(Default, gtk::CompositeTemplate)] @@ -91,19 +88,15 @@ mod imp { let manager = PwvucontrolManager::default(); let core = manager.imp().wp_core.get().expect("Core"); - let defaultnodesapi = - wp::plugin::Plugin::find(core, "default-nodes-api").expect("Get mixer-api"); + let defaultnodesapi = wp::plugin::Plugin::find(core, "default-nodes-api").expect("Get mixer-api"); let type_name = match node.nodetype() { NodeType::Sink => "Audio/Sink", NodeType::Source => "Audio/Source", - _ => unreachable!() + _ => unreachable!(), }; - let result: bool = defaultnodesapi.emit_by_name( - "set-default-configured-node-name", - &[&type_name, &node_name], - ); + let result: bool = defaultnodesapi.emit_by_name("set-default-configured-node-name", &[&type_name, &node_name]); wp::info!("set-default-configured-node-name result: {result:?}"); } } @@ -117,9 +110,7 @@ glib::wrapper! { impl PwSinkBox { pub(crate) fn new(node_object: &impl glib::IsA) -> Self { - glib::Object::builder() - .property("node-object", node_object) - .build() + glib::Object::builder().property("node-object", node_object).build() } pub(crate) fn default_node_changed(&self) { @@ -128,9 +119,7 @@ impl PwSinkBox { let id = self.default_node(); imp.block_default_node_toggle_signal.set(true); - self.imp() - .default_sink_toggle - .set_active(node.boundid() == id); + self.imp().default_sink_toggle.set_active(node.boundid() == id); imp.block_default_node_toggle_signal.set(false); } } diff --git a/src/ui/volumebox.rs b/src/ui/volumebox.rs index 2b59f1a..3c6e372 100644 --- a/src/ui/volumebox.rs +++ b/src/ui/volumebox.rs @@ -1,17 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use crate::{ - backend::PwvucontrolManager, - backend::PwNodeObject, - ui::PwChannelBox, - ui::LevelbarProvider, - backend::PwChannelObject, -}; - -use glib::{clone, ControlFlow, closure_local, SignalHandlerId}; +use crate::{backend::PwChannelObject, backend::PwNodeObject, backend::PwvucontrolManager, ui::LevelbarProvider, ui::PwChannelBox}; + +use glib::{clone, closure_local, ControlFlow, SignalHandlerId}; use gtk::{prelude::*, subclass::prelude::*}; -use std::cell::{Cell, RefCell}; use once_cell::sync::OnceCell; +use std::cell::{Cell, RefCell}; use wireplumber as wp; mod imp { @@ -23,14 +17,14 @@ mod imp { pub struct PwVolumeBox { #[property(get, set, construct_only)] pub(super) node_object: RefCell>, - + metadata_changed_event: Cell>, levelbarprovider: OnceCell, timeoutid: Cell>, pub(super) level: Cell, pub(super) default_node: Cell, pub(super) default_node_changed_handler: RefCell>>, - + // Template widgets #[template_child] pub icon: TemplateChild, @@ -58,64 +52,61 @@ mod imp { pub monitorvolumescale: TemplateChild, #[template_child] pub container: TemplateChild, - } - + #[glib::object_subclass] impl ObjectSubclass for PwVolumeBox { const NAME: &'static str = "PwVolumeBox"; type Type = super::PwVolumeBox; type ParentType = gtk::ListBoxRow; type Interfaces = (gtk::Buildable,); - + fn class_init(klass: &mut Self::Class) { klass.bind_template(); klass.bind_template_callbacks(); } - + fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } } - + #[glib::derived_properties] impl ObjectImpl for PwVolumeBox { fn constructed(&self) { fn linear_to_cubic(_binding: &glib::Binding, i: f32) -> Option { Some(i.cbrt() as f64) } - + fn cubic_to_linear(_binding: &glib::Binding, i: f64) -> Option { Some((i * i * i) as f32) } - + self.parent_constructed(); let item = self.node_object.borrow(); let item = item.as_ref().cloned().unwrap(); - + self.icon.set_icon_name(Some(&item.iconname())); - - item.bind_property("name", &self.title_label.get(), "label") - .sync_create() - .build(); - + + item.bind_property("name", &self.title_label.get(), "label").sync_create().build(); + item.bind_property("description", &self.subtitle_label.get(), "label") .sync_create() .build(); - + item.bind_property("mute", &self.mutebtn.get(), "active") .sync_create() .bidirectional() .build(); - + item.bind_property("volume", &self.volume_scale.adjustment(), "value") .sync_create() .bidirectional() .transform_to(linear_to_cubic) .transform_from(cubic_to_linear) .build(); - + #[rustfmt::skip] item.bind_property("monitorvolume", &self.monitorvolumescale.adjustment(), "value") .sync_create() @@ -123,27 +114,21 @@ mod imp { .transform_to(linear_to_cubic) .transform_from(cubic_to_linear) .build(); - + self.volume_scale.set_format_value_func(|_scale, value| { format!( "{:>16}", - format!( - "{:.0}% ({:.2} dB)", - value * 100.0, - (value * value * value).log10() * 20.0 - ) + format!("{:.0}% ({:.2} dB)", value * 100.0, (value * value * value).log10() * 20.0) ) }); - - item.bind_property("formatstr", &self.format.get(), "label") - .sync_create() - .build(); - + + item.bind_property("formatstr", &self.format.get(), "label").sync_create().build(); + item.bind_property("channellock", &self.channellock.get(), "active") .sync_create() .bidirectional() .build(); - + item.bind_property("mainvolume", &self.mainvolumescale.adjustment(), "value") .sync_create() .bidirectional() @@ -178,22 +163,21 @@ mod imp { .upcast::() }), ); - - self.revealer - .connect_child_revealed_notify(clone!(@weak self as widget => move |_| { - widget.obj().grab_focus(); - })); - + + self.revealer.connect_child_revealed_notify(clone!(@weak self as widget => move |_| { + widget.obj().grab_focus(); + })); + self.level_bar.set_min_value(0.0); self.level_bar.set_max_value(1.0); - + self.level_bar.add_offset_value(gtk::LEVEL_BAR_OFFSET_LOW, 0.0); self.level_bar.add_offset_value(gtk::LEVEL_BAR_OFFSET_HIGH, 0.0); self.level_bar.add_offset_value(gtk::LEVEL_BAR_OFFSET_FULL, 1.0); - + if let Ok(provider) = LevelbarProvider::new(&self.obj(), item.boundid()) { self.levelbarprovider.set(provider).expect("Provider not set already"); - + self.timeoutid.set(Some(glib::timeout_add_local( std::time::Duration::from_millis(25), clone!(@weak self as obj => @default-panic, move || { @@ -203,7 +187,7 @@ mod imp { ))); } } - + fn dispose(&self) { if let Some(sid) = self.metadata_changed_event.take() { let manager = PwvucontrolManager::default(); @@ -234,7 +218,7 @@ mod imp { } } } - + #[gtk::template_callbacks] impl PwVolumeBox { #[template_callback] @@ -269,7 +253,7 @@ impl PwVolumeBox { handler.replace(Box::new(c)); } } -pub trait PwVolumeBoxImpl: ListBoxRowImpl {} +pub trait PwVolumeBoxImpl: ListBoxRowImpl {} pub trait PwVolumeBoxExt: IsA { fn default_node(&self) -> u32 { @@ -290,6 +274,5 @@ impl> PwVolumeBoxExt for O {} unsafe impl IsSubclassable for PwVolumeBox { fn class_init(class: &mut glib::Class) { Self::parent_class_init::(class.upcast_ref_mut()); - } } diff --git a/src/ui/window.rs b/src/ui/window.rs index a2736db..0704baf 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -1,26 +1,19 @@ // SPDX-License-Identifier: GPL-3.0-or-later -use gtk::{ - gio, - prelude::*, - subclass::prelude::*, -}; -use glib::clone; -use adw::subclass::prelude::*; -use crate::{backend::{PwDeviceObject, PwvucontrolManager}, ui::devicebox::PwDeviceBox}; +use crate::macros::*; use crate::{ application::PwvucontrolApplication, - ui::PwVolumeBox, - backend::PwNodeObject, - ui::PwSinkBox, - ui::PwOutputBox, - config::{APP_ID, PROFILE} + backend::{PwDeviceObject, PwNodeObject, PwvucontrolManager}, + config::{APP_ID, PROFILE}, + ui::{devicebox::PwDeviceBox, PwOutputBox, PwSinkBox, PwVolumeBox}, }; -use crate::macros::*; +use adw::subclass::prelude::*; +use glib::clone; +use gtk::{gio, prelude::*}; pub enum PwvucontrolWindowView { Connected, - Disconnected + Disconnected, } mod imp { use super::*; @@ -62,7 +55,7 @@ mod imp { cardlist: TemplateChild::default(), viewstack: TemplateChild::default(), reconnectbtn: TemplateChild::default(), - settings: gio::Settings::new(APP_ID) + settings: gio::Settings::new(APP_ID), } } } @@ -75,7 +68,7 @@ mod imp { fn class_init(klass: &mut Self::Class) { PwVolumeBox::ensure_type(); - + klass.bind_template(); klass.bind_template_callbacks(); } @@ -83,7 +76,6 @@ mod imp { fn instance_init(obj: &glib::subclass::InitializingObject) { obj.init_template(); } - } impl ObjectImpl for PwvucontrolWindow { @@ -162,7 +154,6 @@ mod imp { }); self.obj().load_window_state(); - } } impl WidgetImpl for PwvucontrolWindow {} @@ -180,8 +171,7 @@ mod imp { impl AdwApplicationWindowImpl for PwvucontrolWindow {} #[gtk::template_callbacks] - impl PwvucontrolWindow { - } + impl PwvucontrolWindow {} } glib::wrapper! { @@ -192,9 +182,7 @@ glib::wrapper! { impl PwvucontrolWindow { pub fn new(application: &PwvucontrolApplication) -> Self { - glib::Object::builder() - .property("application", application) - .build() + glib::Object::builder().property("application", application).build() } pub(crate) fn set_view(&self, view: PwvucontrolWindowView) { @@ -203,7 +191,6 @@ impl PwvucontrolWindow { PwvucontrolWindowView::Connected => imp.viewstack.set_visible_child_name("connected"), PwvucontrolWindowView::Disconnected => imp.viewstack.set_visible_child_name("disconnected"), } - } fn save_window_size(&self) -> Result<(), glib::BoolError> { @@ -231,44 +218,44 @@ impl PwvucontrolWindow { if is_maximized { self.maximize(); } - } /// This prevents child widgets from capturing scroll events fn setup_scroll_blocker(&self, listbox: >k::ListBox) { - let scrolledwindow = listbox.ancestor(gtk::ScrolledWindow::static_type()).and_then(|x|{ - x.downcast::().ok() - }).expect("downcast to scrolled window"); - - let ecs = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL); - ecs.set_propagation_phase(gtk::PropagationPhase::Capture); - ecs.set_propagation_limit(gtk::PropagationLimit::SameNative); - - // Need to actually handle the scroll event in order to block propagation - ecs.connect_local("scroll", false, clone!(@weak scrolledwindow => @default-return None, move |v| { + let scrolledwindow = listbox + .ancestor(gtk::ScrolledWindow::static_type()) + .and_then(|x| x.downcast::().ok()) + .expect("downcast to scrolled window"); + + let ecs = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL); + ecs.set_propagation_phase(gtk::PropagationPhase::Capture); + ecs.set_propagation_limit(gtk::PropagationLimit::SameNative); + + // Need to actually handle the scroll event in order to block propagation + ecs.connect_local( + "scroll", + false, + clone!(@weak scrolledwindow => @default-return None, move |v| { let y: f64 = v.get(2).unwrap().get().unwrap(); // No way to redirect this event to underlying widget so we need to reimplement the scroll handling let adjustment = scrolledwindow.vadjustment(); - + if (adjustment.upper() - adjustment.page_size()).abs() < f64::EPSILON { return Some(false.to_value()); } - + adjustment.set_value(adjustment.value() + y*adjustment.page_size().powf(2.0 / 3.0)); - + Some(true.to_value()) - })); - scrolledwindow.add_controller(ecs); + }), + ); + scrolledwindow.add_controller(ecs); } } impl Default for PwvucontrolWindow { fn default() -> Self { - PwvucontrolApplication::default() - .active_window() - .unwrap() - .downcast() - .unwrap() + PwvucontrolApplication::default().active_window().unwrap().downcast().unwrap() } }