qt_core/
connect.rs

1use crate::{q_meta_object::Connection, ConnectionType, QBox, QObject, QPtr};
2use cpp_core::{CastInto, CppBox, CppDeletable, Ptr, Ref, StaticUpcast};
3use std::ffi::CStr;
4use std::fmt;
5use std::marker::PhantomData;
6
7/// Argument types compatible for signal connection.
8///
9/// Qt allows to connect senders to receivers if their argument types are the same.
10/// Additionally, Qt allows receivers to have fewer arguments than the sender.
11/// Other arguments are simply omitted in such a connection.
12///
13/// Note that Qt also allows to connect senders to receivers when their argument types
14/// are not the same but there is a conversion from sender's argument types
15/// to receiver's corresponding argument types. This ability is not exposed in Rust
16/// wrapper's API.
17///
18/// Argument types are expressed as a tuple.
19/// `ArgumentsCompatible<T1>` is implemented for `T2` tuple if
20/// `T1` tuple can be constructed by removing some elements from the end of `T2`.
21///
22/// For instance, `ArgumentsCompatible<T>` and `ArgumentsCompatible<()>` are implemented
23/// for every `T`.
24///
25/// `ArgumentsCompatible` is implemented for tuples with up to 16 items.
26pub trait ArgumentsCompatible<T> {}
27
28/// Reference to a particular signal or slot of a particular object.
29///
30/// A `Receiver` can be used as the receiving side of a Qt signal connection.
31/// The `Arguments` generic argument specifies argument types of this signal or slot.
32pub struct Receiver<Arguments> {
33    q_object: Ref<QObject>,
34    receiver_id: &'static CStr,
35    _marker: PhantomData<Arguments>,
36}
37
38impl<A> Clone for Receiver<A> {
39    fn clone(&self) -> Self {
40        Receiver {
41            q_object: self.q_object,
42            receiver_id: self.receiver_id,
43            _marker: PhantomData,
44        }
45    }
46}
47impl<A> Copy for Receiver<A> {}
48
49impl<A> fmt::Debug for Receiver<A> {
50    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
51        fmt.debug_struct("Receiver")
52            .field("q_object", &self.q_object)
53            .field("receiver_id", &self.receiver_id)
54            .finish()
55    }
56}
57
58impl<A> Receiver<A> {
59    /// Creates a `Receiver` than references a signal or a slot of `q_object` identified by
60    /// `receiver_id`.
61    ///
62    /// This function should not be used manually. It's normally called from functions
63    /// generated by `ritual`. `receiver_id` is the ID returned by Qt's `SIGNAL` and `SLOT`
64    /// C++ macros.
65    ///
66    /// # Safety
67    ///
68    /// `q_object` must contain a valid pointer to a `QObject`-based object. The object
69    /// must outlive the created `Receiver` object.
70    pub unsafe fn new(q_object: impl CastInto<Ref<QObject>>, receiver_id: &'static CStr) -> Self {
71        Self {
72            q_object: q_object.cast_into(),
73            receiver_id,
74            _marker: PhantomData,
75        }
76    }
77}
78
79/// Reference to a particular signal of a particular object.
80///
81/// The `Arguments` generic argument specifies argument types of this signal.
82pub struct Signal<Arguments>(Receiver<Arguments>);
83
84impl<A> Clone for Signal<A> {
85    fn clone(&self) -> Self {
86        Signal(self.0)
87    }
88}
89
90impl<A> Copy for Signal<A> {}
91
92impl<A> fmt::Debug for Signal<A> {
93    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
94        fmt.debug_struct("Signal")
95            .field("qobject", &self.0.q_object)
96            .field("receiver_id", &self.0.receiver_id)
97            .finish()
98    }
99}
100
101impl<A> Signal<A> {
102    /// Creates a `Signal` than references a signal of `q_object` identified by
103    /// `receiver_id`.
104    ///
105    /// This function should not be used manually. It's normally called from functions
106    /// generated by `ritual`. `receiver_id` is the ID returned by Qt's `SIGNAL`
107    /// C++ macro.
108    ///
109    /// # Safety
110    ///
111    /// `q_object` must contain a valid pointer to a `QObject`-based object. The object
112    /// must outlive the created `Signal` object.
113    pub unsafe fn new(q_object: impl CastInto<Ref<QObject>>, receiver_id: &'static CStr) -> Self {
114        Signal(Receiver::new(q_object, receiver_id))
115    }
116}
117
118/// Values that can be used for specifying the receiving side of a Qt signal connection.
119pub trait AsReceiver {
120    /// Argument types expected by this receiver.
121    type Arguments;
122    /// Returns information about this receiver.
123    fn as_receiver(&self) -> Receiver<Self::Arguments>;
124}
125
126impl<A> AsReceiver for Receiver<A> {
127    type Arguments = A;
128    fn as_receiver(&self) -> Receiver<A> {
129        *self
130    }
131}
132
133impl<A> AsReceiver for Signal<A> {
134    type Arguments = A;
135    fn as_receiver(&self) -> Receiver<A> {
136        self.0
137    }
138}
139
140impl<T> AsReceiver for Ptr<T>
141where
142    T: AsReceiver,
143{
144    type Arguments = <T as AsReceiver>::Arguments;
145    fn as_receiver(&self) -> Receiver<Self::Arguments> {
146        (**self).as_receiver()
147    }
148}
149
150impl<T> AsReceiver for Ref<T>
151where
152    T: AsReceiver,
153{
154    type Arguments = <T as AsReceiver>::Arguments;
155    fn as_receiver(&self) -> Receiver<Self::Arguments> {
156        (**self).as_receiver()
157    }
158}
159
160impl<'a, T: CppDeletable> AsReceiver for &'a CppBox<T>
161where
162    T: AsReceiver,
163{
164    type Arguments = <T as AsReceiver>::Arguments;
165    fn as_receiver(&self) -> Receiver<Self::Arguments> {
166        (***self).as_receiver()
167    }
168}
169
170impl<'a, T: StaticUpcast<QObject> + CppDeletable> AsReceiver for &'a QBox<T>
171where
172    T: AsReceiver,
173{
174    type Arguments = <T as AsReceiver>::Arguments;
175    fn as_receiver(&self) -> Receiver<Self::Arguments> {
176        (***self).as_receiver()
177    }
178}
179
180impl<'a, T: StaticUpcast<QObject>> AsReceiver for &'a QPtr<T>
181where
182    T: AsReceiver,
183{
184    type Arguments = <T as AsReceiver>::Arguments;
185    fn as_receiver(&self) -> Receiver<Self::Arguments> {
186        (***self).as_receiver()
187    }
188}
189
190impl<SignalArguments> Signal<SignalArguments> {
191    /// Creates a Qt connection between this signal and `receiver`, using the specified
192    /// `connection_type`.
193    ///
194    /// Returns an object that represents the connection. You can use `is_valid()` on this object
195    /// to determine if the connection was successful.
196    ///
197    /// The connection will automatically be deleted when either the sender or the receiver
198    /// is deleted.
199    ///
200    /// # Safety
201    ///
202    /// The `QObject`s referenced by `self` and `receiver` must be alive.
203    ///
204    /// [C++ documentation](https://doc.qt.io/qt-5/qobject.html#connect):
205    /// <div style='border: 1px solid #5CFF95; background: #D6FFE4; padding: 16px;'>
206    /// <p>Creates a connection of the given&nbsp;<em>type</em>&nbsp;from the&nbsp;<em>signal</em>&nbsp;in the&nbsp;<em>sender</em>&nbsp;object to the&nbsp;<em>method</em>&nbsp;in the&nbsp;<em>receiver</em>&nbsp;object. Returns a handle to the connection that can be used to disconnect it later.</p>
207    /// <p>You must use the&nbsp;<code>SIGNAL()</code>&nbsp;and&nbsp;<code>SLOT()</code>&nbsp;macros when specifying the&nbsp;<em>signal</em>&nbsp;and the&nbsp;<em>method</em>, for example:</p>
208    /// <div>
209    /// <pre><a href="https://doc.qt.io/qt-5/qlabel.html">QLabel</a> *label = new <a href="https://doc.qt.io/qt-5/qlabel.html">QLabel</a>;
210    /// <a href="https://doc.qt.io/qt-5/qscrollbar.html">QScrollBar</a> *scrollBar = new <a href="https://doc.qt.io/qt-5/qscrollbar.html">QScrollBar</a>;
211    /// <a href="https://doc.qt.io/qt-5/qobject.html#QObject">QObject</a>::connect(scrollBar, SIGNAL(valueChanged(int)),
212    ///                  label,  SLOT(setNum(int)));</pre>
213    /// </div>
214    /// <p>This example ensures that the label always displays the current scroll bar value. Note that the signal and slots parameters must not contain any variable names, only the type. E.g. the following would not work and return false:</p>
215    /// <div>
216    /// <pre>// WRONG
217    /// <a href="https://doc.qt.io/qt-5/qobject.html#QObject">QObject</a>::connect(scrollBar, SIGNAL(valueChanged(int value)),
218    ///                  label, SLOT(setNum(int value)));</pre>
219    /// </div>
220    /// <p>A signal can also be connected to another signal:</p>
221    /// <div>
222    /// <pre>class MyWidget : public <a href="https://doc.qt.io/qt-5/qwidget.html">QWidget</a>
223    /// {
224    ///     Q_OBJECT
225    /// public:
226    ///     MyWidget();
227    /// signals:
228    ///     buttonClicked();
229    /// private:
230    ///     <a href="https://doc.qt.io/qt-5/qpushbutton.html">QPushButton</a> *myButton;
231    /// };
232    /// MyWidget::MyWidget()
233    /// {
234    ///     myButton = new <a href="https://doc.qt.io/qt-5/qpushbutton.html">QPushButton</a>(this);
235    ///     connect(myButton, SIGNAL(clicked()),
236    ///             this, SIGNAL(buttonClicked()));
237    /// }</pre>
238    /// </div>
239    /// <p>In this example, the&nbsp;<code>MyWidget</code>&nbsp;constructor relays a signal from a private member variable, and makes it available under a name that relates to&nbsp;<code>MyWidget</code>.</p>
240    /// <p>A signal can be connected to many slots and signals. Many signals can be connected to one slot.</p>
241    /// <p>If a signal is connected to several slots, the slots are activated in the same order in which the connections were made, when the signal is emitted.</p>
242    /// <p>The function returns a&nbsp;<a href="https://doc.qt.io/qt-5/qmetaobject-connection.html">QMetaObject::Connection</a>&nbsp;that represents a handle to a connection if it successfully connects the signal to the slot. The connection handle will be invalid if it cannot create the connection, for example, if&nbsp;<a href="https://doc.qt.io/qt-5/qobject.html">QObject</a>&nbsp;is unable to verify the existence of either&nbsp;<em>signal</em>&nbsp;or&nbsp;<em>method</em>, or if their signatures aren't compatible. You can check if the handle is valid by casting it to a bool.</p>
243    /// <p>By default, a signal is emitted for every connection you make; two signals are emitted for duplicate connections. You can break all of these connections with a single&nbsp;<a href="https://doc.qt.io/qt-5/qobject.html#disconnect">disconnect</a>() call. If you pass the&nbsp;<a href="https://doc.qt.io/qt-5/qt.html#ConnectionType-enum">Qt::UniqueConnection</a>&nbsp;<em>type</em>, the connection will only be made if it is not a duplicate. If there is already a duplicate (exact same signal to the exact same slot on the same objects), the connection will fail and connect will return an invalid&nbsp;<a href="https://doc.qt.io/qt-5/qmetaobject-connection.html">QMetaObject::Connection</a>.</p>
244    /// <p><strong>Note:&nbsp;</strong>Qt::UniqueConnections do not work for lambdas, non-member functions and functors; they only apply to connecting to member functions.</p>
245    /// <p>The optional&nbsp;<em>type</em>&nbsp;parameter describes the type of connection to establish. In particular, it determines whether a particular signal is delivered to a slot immediately or queued for delivery at a later time. If the signal is queued, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them in an event behind the scenes. If you try to use a queued connection and get the error message</p>
246    /// <div>
247    /// <pre><a href="https://doc.qt.io/qt-5/qobject.html#QObject">QObject</a>::connect: Cannot queue arguments of type 'MyType'
248    /// (Make sure 'MyType' is registered using <a href="https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1">qRegisterMetaType</a>().)</pre>
249    /// </div>
250    /// <p>call&nbsp;<a href="https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1">qRegisterMetaType</a>() to register the data type before you establish the connection.</p>
251    /// <p><strong>Note:</strong>&nbsp;This function is&nbsp;<a href="https://doc.qt.io/qt-5/threads-reentrancy.html">thread-safe</a>.</p>
252    /// <p><strong>See also&nbsp;</strong><a href="https://doc.qt.io/qt-5/qobject.html#disconnect">disconnect</a>(),&nbsp;<a href="https://doc.qt.io/qt-5/qobject.html#sender">sender</a>(),&nbsp;<a href="https://doc.qt.io/qt-5/qmetatype.html#qRegisterMetaType-1">qRegisterMetaType</a>(),&nbsp;<a href="https://doc.qt.io/qt-5/qmetatype.html#Q_DECLARE_METATYPE">Q_DECLARE_METATYPE</a>(), and&nbsp;<a href="https://doc.qt.io/qt-5/signalsandslots-syntaxes.html">Differences between String-Based and Functor-Based Connections</a>.</p>
253    /// </div>
254    pub unsafe fn connect_with_type<R>(
255        &self,
256        connection_type: ConnectionType,
257        receiver: R,
258    ) -> CppBox<Connection>
259    where
260        R: AsReceiver,
261        SignalArguments: ArgumentsCompatible<R::Arguments>,
262    {
263        let receiver = receiver.as_receiver();
264
265        crate::QObject::connect_5a(
266            self.0.q_object.as_ptr(),
267            self.0.receiver_id.as_ptr(),
268            receiver.q_object.as_ptr(),
269            receiver.receiver_id.as_ptr(),
270            connection_type,
271        )
272    }
273
274    /// Creates a Qt connection between this signal and `receiver`, using auto connection type.
275    ///
276    /// See `connect_with_type` for more details.
277    pub unsafe fn connect<R>(&self, receiver: R) -> CppBox<Connection>
278    where
279        R: AsReceiver,
280        SignalArguments: ArgumentsCompatible<R::Arguments>,
281    {
282        self.connect_with_type(ConnectionType::AutoConnection, receiver)
283    }
284}