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