gio/subclass/
file_monitor.rs1use glib::{prelude::*, subclass::prelude::*, translate::*};
4
5use crate::{ffi, File, FileMonitor, FileMonitorEvent};
6
7pub trait FileMonitorImpl: ObjectImpl + ObjectSubclass<Type: IsA<FileMonitor>> {
9 fn changed(&self, file: &File, other_file: Option<&File>, event_type: FileMonitorEvent) {
10 self.parent_changed(file, other_file, event_type)
11 }
12
13 fn cancel(&self) {
19 self.parent_cancel()
20 }
21}
22
23pub trait FileMonitorImplExt: FileMonitorImpl {
25 fn parent_changed(&self, file: &File, other_file: Option<&File>, event_type: FileMonitorEvent) {
26 unsafe {
27 let data = Self::type_data();
28 let parent_class = data.as_ref().parent_class() as *const ffi::GFileMonitorClass;
29
30 if let Some(f) = (*parent_class).changed {
31 f(
32 self.obj().unsafe_cast_ref::<FileMonitor>().to_glib_none().0,
33 file.to_glib_none().0,
34 other_file.to_glib_none().0,
35 event_type.into_glib(),
36 );
37 }
38 }
39 }
40
41 fn parent_cancel(&self) {
42 unsafe {
43 let data = Self::type_data();
44 let parent_class = data.as_ref().parent_class() as *const ffi::GFileMonitorClass;
45
46 let f = (*parent_class)
47 .cancel
48 .expect("No parent class implementation for \"cancel\"");
49
50 let _ = f(self.obj().unsafe_cast_ref::<FileMonitor>().to_glib_none().0);
51 }
52 }
53}
54
55impl<T: FileMonitorImpl> FileMonitorImplExt for T {}
56
57unsafe impl<T: FileMonitorImpl> IsSubclassable<T> for FileMonitor {
59 fn class_init(class: &mut ::glib::Class<Self>) {
60 Self::parent_class_init::<T>(class);
61
62 let klass = class.as_mut();
63 klass.changed = Some(changed::<T>);
64 klass.cancel = Some(cancel::<T>);
65 }
66}
67
68unsafe extern "C" fn changed<T: FileMonitorImpl>(
69 monitor: *mut ffi::GFileMonitor,
70 file: *mut ffi::GFile,
71 other_file: *mut ffi::GFile,
72 event_type: ffi::GFileMonitorEvent,
73) {
74 let instance = &*(monitor as *mut T::Instance);
75 let imp = instance.imp();
76 let other_file = Option::<File>::from_glib_none(other_file);
77
78 imp.changed(
79 &from_glib_borrow(file),
80 other_file.as_ref(),
81 from_glib(event_type),
82 );
83}
84
85unsafe extern "C" fn cancel<T: FileMonitorImpl>(
86 monitor: *mut ffi::GFileMonitor,
87) -> glib::ffi::gboolean {
88 let instance = &*(monitor as *mut T::Instance);
89 let imp = instance.imp();
90
91 imp.cancel();
92
93 true.into_glib()
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
104 use crate::prelude::*;
105
106 mod imp {
108 use super::*;
109
110 #[derive(Default)]
111 pub struct MyFileMonitor;
112
113 #[glib::object_subclass]
114 impl ObjectSubclass for MyFileMonitor {
115 const NAME: &'static str = "MyFileMonitor";
116 type Type = super::MyFileMonitor;
117 type ParentType = FileMonitor;
118 }
119
120 impl ObjectImpl for MyFileMonitor {}
121
122 impl FileMonitorImpl for MyFileMonitor {
124 fn cancel(&self) {}
125 }
126
127 #[derive(Default)]
128 pub struct MyCustomFileMonitor;
129
130 #[glib::object_subclass]
131 impl ObjectSubclass for MyCustomFileMonitor {
132 const NAME: &'static str = "MyCustomFileMonitor";
133 type Type = super::MyCustomFileMonitor;
134 type ParentType = super::MyFileMonitor;
135 }
136
137 impl ObjectImpl for MyCustomFileMonitor {}
138
139 impl FileMonitorImpl for MyCustomFileMonitor {}
141
142 impl MyFileMonitorImpl for MyCustomFileMonitor {}
143 }
144
145 glib::wrapper! {
146 pub struct MyFileMonitor(ObjectSubclass<imp::MyFileMonitor>) @extends FileMonitor;
147 }
148
149 pub trait MyFileMonitorImpl:
150 ObjectImpl + ObjectSubclass<Type: IsA<MyFileMonitor> + IsA<FileMonitor>>
151 {
152 }
153
154 unsafe impl<T: MyFileMonitorImpl + FileMonitorImpl> IsSubclassable<T> for MyFileMonitor {}
156
157 glib::wrapper! {
158 pub struct MyCustomFileMonitor(ObjectSubclass<imp::MyCustomFileMonitor>) @extends MyFileMonitor, FileMonitor;
159 }
160
161 #[test]
162 fn file_monitor_changed() {
163 let _ = glib::MainContext::new().with_thread_default(|| {
165 let my_custom_file_monitor = glib::Object::new::<MyCustomFileMonitor>();
167 let rx = {
168 let (tx, rx) = async_channel::bounded(1);
169 my_custom_file_monitor.connect_changed(move |_, file, other_file, event_type| {
170 let res = glib::MainContext::ref_thread_default().block_on(tx.send((
171 file.uri(),
172 other_file.map(File::uri),
173 event_type,
174 )));
175 assert!(res.is_ok(), "{}", res.err().unwrap());
176 });
177 rx
178 };
179 my_custom_file_monitor.emit_event(
181 &File::for_uri("child"),
182 None::<&File>,
183 FileMonitorEvent::Created,
184 );
185 let res = glib::MainContext::ref_thread_default().block_on(rx.recv());
186 assert!(res.is_ok(), "{}", res.err().unwrap());
187 let event = res.unwrap();
188
189 let my_file_monitor = glib::Object::new::<MyFileMonitor>();
191 let expected_rx = {
192 let (tx, rx) = async_channel::bounded(1);
193 my_file_monitor.connect_changed(move |_, file, other_file, event_type| {
194 let res = glib::MainContext::ref_thread_default().block_on(tx.send((
195 file.uri(),
196 other_file.map(File::uri),
197 event_type,
198 )));
199 assert!(res.is_ok(), "{}", res.err().unwrap());
200 });
201 rx
202 };
203 my_file_monitor.emit_event(
205 &File::for_uri("child"),
206 None::<&File>,
207 FileMonitorEvent::Created,
208 );
209 let res = glib::MainContext::ref_thread_default().block_on(expected_rx.recv());
210 assert!(res.is_ok(), "{}", res.err().unwrap());
211 let expected_event = res.unwrap();
212
213 assert_eq!(event, expected_event);
215 });
216 }
217
218 #[test]
219 fn file_monitor_cancel() {
220 let _ = glib::MainContext::new().with_thread_default(|| {
222 let my_custom_file_monitor = glib::Object::new::<MyCustomFileMonitor>();
224 let rx = {
225 let (tx, rx) = async_channel::bounded(1);
226 my_custom_file_monitor.connect_cancelled_notify(move |_| {
227 let res = glib::MainContext::ref_thread_default().block_on(tx.send(true));
228 assert!(res.is_ok(), "{}", res.err().unwrap());
229 });
230 rx
231 };
232 let cancelled = my_custom_file_monitor.cancel();
233 let res = glib::MainContext::ref_thread_default().block_on(rx.recv());
234 assert!(res.is_ok(), "{}", res.err().unwrap());
235 let notified = res.unwrap();
236 assert_eq!(cancelled, notified);
237
238 let my_file_monitor = glib::Object::new::<MyFileMonitor>();
240 let expected_rx = {
241 let (tx, rx) = async_channel::bounded(1);
242 my_file_monitor.connect_cancelled_notify(move |_| {
243 let res = glib::MainContext::ref_thread_default().block_on(tx.send(true));
244 assert!(res.is_ok(), "{}", res.err().unwrap());
245 });
246 rx
247 };
248 let expected_cancelled = my_file_monitor.cancel();
249 let res = glib::MainContext::ref_thread_default().block_on(expected_rx.recv());
250 assert!(res.is_ok(), "{}", res.err().unwrap());
251 let expected_notified = res.unwrap();
252 assert_eq!(expected_cancelled, expected_notified);
253
254 assert_eq!(cancelled, expected_cancelled);
256 assert_eq!(notified, expected_notified);
257 });
258 }
259}