gio/subclass/
file_monitor.rs1use glib::{prelude::*, subclass::prelude::*, translate::*};
4
5use crate::{File, FileMonitor, FileMonitorEvent, ffi};
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 unsafe {
75 let instance = &*(monitor as *mut T::Instance);
76 let imp = instance.imp();
77 let other_file = Option::<File>::from_glib_none(other_file);
78
79 imp.changed(
80 &from_glib_borrow(file),
81 other_file.as_ref(),
82 from_glib(event_type),
83 );
84 }
85}
86
87unsafe extern "C" fn cancel<T: FileMonitorImpl>(
88 monitor: *mut ffi::GFileMonitor,
89) -> glib::ffi::gboolean {
90 unsafe {
91 let instance = &*(monitor as *mut T::Instance);
92 let imp = instance.imp();
93
94 imp.cancel();
95
96 true.into_glib()
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
108 use crate::prelude::*;
109
110 mod imp {
112 use super::*;
113
114 #[derive(Default)]
115 pub struct MyFileMonitor;
116
117 #[glib::object_subclass]
118 impl ObjectSubclass for MyFileMonitor {
119 const NAME: &'static str = "MyFileMonitor";
120 type Type = super::MyFileMonitor;
121 type ParentType = FileMonitor;
122 }
123
124 impl ObjectImpl for MyFileMonitor {}
125
126 impl FileMonitorImpl for MyFileMonitor {
128 fn cancel(&self) {}
129 }
130
131 #[derive(Default)]
132 pub struct MyCustomFileMonitor;
133
134 #[glib::object_subclass]
135 impl ObjectSubclass for MyCustomFileMonitor {
136 const NAME: &'static str = "MyCustomFileMonitor";
137 type Type = super::MyCustomFileMonitor;
138 type ParentType = super::MyFileMonitor;
139 }
140
141 impl ObjectImpl for MyCustomFileMonitor {}
142
143 impl FileMonitorImpl for MyCustomFileMonitor {}
145
146 impl MyFileMonitorImpl for MyCustomFileMonitor {}
147 }
148
149 glib::wrapper! {
150 pub struct MyFileMonitor(ObjectSubclass<imp::MyFileMonitor>) @extends FileMonitor;
151 }
152
153 pub trait MyFileMonitorImpl:
154 ObjectImpl + ObjectSubclass<Type: IsA<MyFileMonitor> + IsA<FileMonitor>>
155 {
156 }
157
158 unsafe impl<T: MyFileMonitorImpl + FileMonitorImpl> IsSubclassable<T> for MyFileMonitor {}
160
161 glib::wrapper! {
162 pub struct MyCustomFileMonitor(ObjectSubclass<imp::MyCustomFileMonitor>) @extends MyFileMonitor, FileMonitor;
163 }
164
165 #[test]
166 fn file_monitor_changed() {
167 let _ = glib::MainContext::new().with_thread_default(|| {
169 let my_custom_file_monitor = glib::Object::new::<MyCustomFileMonitor>();
171 let rx = {
172 let (tx, rx) = async_channel::bounded(1);
173 my_custom_file_monitor.connect_changed(move |_, file, other_file, event_type| {
174 let res = glib::MainContext::ref_thread_default().block_on(tx.send((
175 file.uri(),
176 other_file.map(File::uri),
177 event_type,
178 )));
179 assert!(res.is_ok(), "{}", res.err().unwrap());
180 });
181 rx
182 };
183 my_custom_file_monitor.emit_event(
185 &File::for_uri("child"),
186 None::<&File>,
187 FileMonitorEvent::Created,
188 );
189 let res = glib::MainContext::ref_thread_default().block_on(rx.recv());
190 assert!(res.is_ok(), "{}", res.err().unwrap());
191 let event = res.unwrap();
192
193 let my_file_monitor = glib::Object::new::<MyFileMonitor>();
195 let expected_rx = {
196 let (tx, rx) = async_channel::bounded(1);
197 my_file_monitor.connect_changed(move |_, file, other_file, event_type| {
198 let res = glib::MainContext::ref_thread_default().block_on(tx.send((
199 file.uri(),
200 other_file.map(File::uri),
201 event_type,
202 )));
203 assert!(res.is_ok(), "{}", res.err().unwrap());
204 });
205 rx
206 };
207 my_file_monitor.emit_event(
209 &File::for_uri("child"),
210 None::<&File>,
211 FileMonitorEvent::Created,
212 );
213 let res = glib::MainContext::ref_thread_default().block_on(expected_rx.recv());
214 assert!(res.is_ok(), "{}", res.err().unwrap());
215 let expected_event = res.unwrap();
216
217 assert_eq!(event, expected_event);
219 });
220 }
221
222 #[test]
223 fn file_monitor_cancel() {
224 let _ = glib::MainContext::new().with_thread_default(|| {
226 let my_custom_file_monitor = glib::Object::new::<MyCustomFileMonitor>();
228 let rx = {
229 let (tx, rx) = async_channel::bounded(1);
230 my_custom_file_monitor.connect_cancelled_notify(move |_| {
231 let res = glib::MainContext::ref_thread_default().block_on(tx.send(true));
232 assert!(res.is_ok(), "{}", res.err().unwrap());
233 });
234 rx
235 };
236 let cancelled = my_custom_file_monitor.cancel();
237 let res = glib::MainContext::ref_thread_default().block_on(rx.recv());
238 assert!(res.is_ok(), "{}", res.err().unwrap());
239 let notified = res.unwrap();
240 assert_eq!(cancelled, notified);
241
242 let my_file_monitor = glib::Object::new::<MyFileMonitor>();
244 let expected_rx = {
245 let (tx, rx) = async_channel::bounded(1);
246 my_file_monitor.connect_cancelled_notify(move |_| {
247 let res = glib::MainContext::ref_thread_default().block_on(tx.send(true));
248 assert!(res.is_ok(), "{}", res.err().unwrap());
249 });
250 rx
251 };
252 let expected_cancelled = my_file_monitor.cancel();
253 let res = glib::MainContext::ref_thread_default().block_on(expected_rx.recv());
254 assert!(res.is_ok(), "{}", res.err().unwrap());
255 let expected_notified = res.unwrap();
256 assert_eq!(expected_cancelled, expected_notified);
257
258 assert_eq!(cancelled, expected_cancelled);
260 assert_eq!(notified, expected_notified);
261 });
262 }
263}