gtk4/subclass/
im_context.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for subclassing [`IMContext`].
5
6use glib::{translate::*, GString};
7use pango::AttrList;
8
9use crate::{ffi, prelude::*, subclass::prelude::*, IMContext, Widget};
10
11#[allow(clippy::upper_case_acronyms)]
12pub trait IMContextImpl: ObjectImpl + ObjectSubclass<Type: IsA<IMContext>> {
13    /// Default handler of the [`commit`][struct@crate::IMContext#commit] signal.
14    fn commit(&self, string: &str) {
15        self.parent_commit(string)
16    }
17    /// Asks the widget that the input context is attached to delete
18    /// characters around the cursor position by emitting the
19    /// `::delete_surrounding` signal.
20    ///
21    /// Note that @offset and @n_chars are in characters not in bytes
22    /// which differs from the usage other places in [`IMContext`][crate::IMContext].
23    ///
24    /// In order to use this function, you should first call
25    /// [`IMContextExt::surrounding()`][crate::prelude::IMContextExt::surrounding()] to get the current context,
26    /// and call this function immediately afterwards to make sure that you
27    /// know what you are deleting. You should also account for the fact
28    /// that even if the signal was handled, the input context might not
29    /// have deleted all the characters that were requested to be deleted.
30    ///
31    /// This function is used by an input method that wants to make
32    /// substitutions in the existing text in response to new input.
33    /// It is not useful for applications.
34    /// ## `offset`
35    /// offset from cursor position in chars;
36    ///    a negative value means start before the cursor.
37    /// ## `n_chars`
38    /// number of characters to delete.
39    ///
40    /// # Returns
41    ///
42    /// [`true`] if the signal was handled.
43    fn delete_surrounding(&self, offset: i32, n_chars: i32) -> bool {
44        self.parent_delete_surrounding(offset, n_chars)
45    }
46    /// Allow an input method to internally handle key press and release
47    /// events.
48    ///
49    /// If this function returns [`true`], then no further processing
50    /// should be done for this key event.
51    /// ## `event`
52    /// the key event
53    ///
54    /// # Returns
55    ///
56    /// [`true`] if the input method handled the key event.
57    fn filter_keypress(&self, event: &gdk::Event) -> bool {
58        self.parent_filter_keypress(event)
59    }
60    /// Notify the input method that the widget to which this
61    /// input context corresponds has gained focus.
62    ///
63    /// The input method may, for example, change the displayed
64    /// feedback to reflect this change.
65    fn focus_in(&self) {
66        self.parent_focus_in()
67    }
68    /// Notify the input method that the widget to which this
69    /// input context corresponds has lost focus.
70    ///
71    /// The input method may, for example, change the displayed
72    /// feedback or reset the contexts state to reflect this change.
73    fn focus_out(&self) {
74        self.parent_focus_out()
75    }
76    /// Retrieve the current preedit string for the input context,
77    /// and a list of attributes to apply to the string.
78    ///
79    /// This string should be displayed inserted at the insertion point.
80    ///
81    /// # Returns
82    ///
83    ///
84    /// ## `str`
85    /// location to store the retrieved
86    ///   string. The string retrieved must be freed with g_free().
87    ///
88    /// ## `attrs`
89    /// location to store the retrieved
90    ///   attribute list. When you are done with this list, you
91    ///   must unreference it with `Pango::AttrList::unref()`.
92    ///
93    /// ## `cursor_pos`
94    /// location to store position of cursor
95    ///   (in characters) within the preedit string.
96    #[doc(alias = "get_preedit_string")]
97    fn preedit_string(&self) -> (GString, AttrList, i32) {
98        self.parent_preedit_string()
99    }
100    /// Retrieves context around the insertion point.
101    ///
102    /// Input methods typically want context in order to constrain input text
103    /// based on existing text; this is important for languages such as Thai
104    /// where only some sequences of characters are allowed.
105    ///
106    /// This function is implemented by emitting the
107    /// [`retrieve-surrounding`][struct@crate::IMContext#retrieve-surrounding] signal on the input method;
108    /// in response to this signal, a widget should provide as much context as
109    /// is available, up to an entire paragraph, by calling
110    /// [`IMContextExt::set_surrounding()`][crate::prelude::IMContextExt::set_surrounding()].
111    ///
112    /// Note that there is no obligation for a widget to respond to the
113    /// `::retrieve-surrounding` signal, so input methods must be prepared to
114    /// function without context.
115    ///
116    /// # Deprecated since 4.2
117    ///
118    /// Use [`IMContextExt::surrounding_with_selection()`][crate::prelude::IMContextExt::surrounding_with_selection()] instead.
119    ///
120    /// # Returns
121    ///
122    /// `TRUE` if surrounding text was provided; in this case
123    ///    you must free the result stored in `text`.
124    ///
125    /// ## `text`
126    /// location to store a UTF-8 encoded
127    ///   string of text holding context around the insertion point.
128    ///   If the function returns [`true`], then you must free the result
129    ///   stored in this location with g_free().
130    ///
131    /// ## `cursor_index`
132    /// location to store byte index of the insertion
133    ///   cursor within @text.
134    #[doc(alias = "get_surrounding")]
135    fn surrounding(&self) -> Option<(GString, i32)> {
136        self.parent_surrounding()
137    }
138    /// Default handler of the [`preedit-changed`][struct@crate::IMContext#preedit-changed]
139    ///   signal.
140    fn preedit_changed(&self) {
141        self.parent_preedit_changed()
142    }
143    /// Default handler of the [`preedit-end`][struct@crate::IMContext#preedit-end] signal.
144    fn preedit_end(&self) {
145        self.parent_preedit_end()
146    }
147    /// Default handler of the [`preedit-start`][struct@crate::IMContext#preedit-start] signal.
148    fn preedit_start(&self) {
149        self.parent_preedit_start()
150    }
151    /// Notify the input method that a change such as a change in cursor
152    /// position has been made.
153    ///
154    /// This will typically cause the input method to clear the preedit state.
155    fn reset(&self) {
156        self.parent_reset()
157    }
158    /// Default handler of the
159    ///   [`retrieve-surrounding`][struct@crate::IMContext#retrieve-surrounding] signal.
160    fn retrieve_surrounding(&self) -> bool {
161        self.parent_retrieve_surrounding()
162    }
163    /// Set the client widget for the input context.
164    ///
165    /// This is the [`Widget`][crate::Widget] holding the input focus. This widget is
166    /// used in order to correctly position status windows, and may
167    /// also be used for purposes internal to the input method.
168    /// ## `widget`
169    /// the client widget. This may be [`None`] to indicate
170    ///   that the previous client widget no longer exists.
171    fn set_client_widget<P: IsA<Widget>>(&self, widget: Option<&P>) {
172        self.parent_set_client_widget(widget)
173    }
174    /// Notify the input method that a change in cursor
175    /// position has been made.
176    ///
177    /// The location is relative to the client widget.
178    /// ## `area`
179    /// new location
180    fn set_cursor_location(&self, area: &gdk::Rectangle) {
181        self.parent_set_cursor_location(area)
182    }
183    /// Sets surrounding context around the insertion point and preedit
184    /// string.
185    ///
186    /// This function is expected to be called in response to the
187    /// [`retrieve-surrounding`][struct@crate::IMContext#retrieve-surrounding] signal, and will
188    /// likely have no effect if called at other times.
189    ///
190    /// # Deprecated since 4.2
191    ///
192    /// Use [`IMContextExt::set_surrounding_with_selection()`][crate::prelude::IMContextExt::set_surrounding_with_selection()] instead
193    /// ## `text`
194    /// text surrounding the insertion point, as UTF-8.
195    ///   the preedit string should not be included within @text
196    /// ## `len`
197    /// the length of @text, or -1 if @text is nul-terminated
198    /// ## `cursor_index`
199    /// the byte index of the insertion cursor within @text.
200    #[cfg_attr(feature = "v4_2", deprecated = "Since 4.2")]
201    #[allow(deprecated)]
202    fn set_surrounding(&self, text: &str, cursor_index: i32) {
203        self.parent_set_surrounding(text, cursor_index)
204    }
205    /// Sets whether the IM context should use the preedit string
206    /// to display feedback.
207    ///
208    /// If @use_preedit is [`false`] (default is [`true`]), then the IM context
209    /// may use some other method to display feedback, such as displaying
210    /// it in a child of the root window.
211    /// ## `use_preedit`
212    /// whether the IM context should use the preedit string.
213    fn set_use_preedit(&self, use_preedit: bool) {
214        self.parent_set_use_preedit(use_preedit)
215    }
216    #[cfg(feature = "v4_10")]
217    #[cfg_attr(docsrs, doc(cfg(feature = "v4_10")))]
218    fn activate_osk(&self) {
219        self.parent_activate_osk()
220    }
221    #[cfg(feature = "v4_14")]
222    #[cfg_attr(docsrs, doc(cfg(feature = "v4_14")))]
223    fn activate_osk_with_event(&self, event: Option<&gdk::Event>) -> bool {
224        self.parent_activate_osk_with_event(event)
225    }
226}
227
228#[allow(clippy::upper_case_acronyms)]
229pub trait IMContextImplExt: IMContextImpl {
230    fn parent_commit(&self, string: &str) {
231        unsafe {
232            let data = Self::type_data();
233            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
234            if let Some(f) = (*parent_class).commit {
235                f(
236                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
237                    string.to_glib_none().0,
238                );
239            }
240        }
241    }
242
243    // Returns true if the signal was handled
244    fn parent_delete_surrounding(&self, offset: i32, n_chars: i32) -> bool {
245        unsafe {
246            let data = Self::type_data();
247            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
248            if let Some(f) = (*parent_class).delete_surrounding {
249                from_glib(f(
250                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
251                    offset,
252                    n_chars,
253                ))
254            } else {
255                false
256            }
257        }
258    }
259
260    // Returns true if the event was consumed
261    fn parent_filter_keypress(&self, event: &gdk::Event) -> bool {
262        unsafe {
263            let data = Self::type_data();
264            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
265            if let Some(f) = (*parent_class).filter_keypress {
266                from_glib(f(
267                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
268                    event.to_glib_none().0,
269                ))
270            } else {
271                false
272            }
273        }
274    }
275
276    fn parent_focus_in(&self) {
277        unsafe {
278            let data = Self::type_data();
279            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
280            if let Some(f) = (*parent_class).focus_in {
281                f(self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0)
282            }
283        }
284    }
285
286    fn parent_focus_out(&self) {
287        unsafe {
288            let data = Self::type_data();
289            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
290            if let Some(f) = (*parent_class).focus_out {
291                f(self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0)
292            }
293        }
294    }
295
296    fn parent_surrounding(&self) -> Option<(GString, i32)> {
297        unsafe {
298            let data = Self::type_data();
299            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
300            if let Some(f) = (*parent_class).get_surrounding {
301                let mut text = std::ptr::null_mut();
302                let mut cursor_index = std::mem::MaybeUninit::uninit();
303                let ret = from_glib(f(
304                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
305                    &mut text,
306                    cursor_index.as_mut_ptr(),
307                ));
308                if ret {
309                    return Some((from_glib_full(text), cursor_index.assume_init()));
310                }
311            }
312            None
313        }
314    }
315
316    fn parent_preedit_string(&self) -> (GString, AttrList, i32) {
317        unsafe {
318            let data = Self::type_data();
319            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
320            let f = (*parent_class)
321                .get_preedit_string
322                .expect("No parent class impl for \"get_preedit_string\"");
323            let mut string = std::ptr::null_mut();
324            let mut attrs = std::ptr::null_mut();
325            let mut cursor_pos = std::mem::MaybeUninit::uninit();
326            f(
327                self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
328                &mut string,
329                &mut attrs,
330                cursor_pos.as_mut_ptr(),
331            );
332            (
333                from_glib_full(string),
334                from_glib_full(attrs),
335                cursor_pos.assume_init(),
336            )
337        }
338    }
339
340    fn parent_preedit_changed(&self) {
341        unsafe {
342            let data = Self::type_data();
343            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
344            if let Some(f) = (*parent_class).preedit_changed {
345                f(self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0)
346            }
347        }
348    }
349
350    fn parent_preedit_end(&self) {
351        unsafe {
352            let data = Self::type_data();
353            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
354            if let Some(f) = (*parent_class).preedit_end {
355                f(self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0)
356            }
357        }
358    }
359
360    fn parent_preedit_start(&self) {
361        unsafe {
362            let data = Self::type_data();
363            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
364            if let Some(f) = (*parent_class).preedit_start {
365                f(self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0)
366            }
367        }
368    }
369
370    fn parent_reset(&self) {
371        unsafe {
372            let data = Self::type_data();
373            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
374            if let Some(f) = (*parent_class).reset {
375                f(self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0)
376            }
377        }
378    }
379
380    // Returns true if the signal was handled
381    fn parent_retrieve_surrounding(&self) -> bool {
382        unsafe {
383            let data = Self::type_data();
384            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
385            if let Some(f) = (*parent_class).retrieve_surrounding {
386                from_glib(f(self
387                    .obj()
388                    .unsafe_cast_ref::<IMContext>()
389                    .to_glib_none()
390                    .0))
391            } else {
392                false
393            }
394        }
395    }
396
397    fn parent_set_client_widget<P: IsA<Widget>>(&self, widget: Option<&P>) {
398        unsafe {
399            let data = Self::type_data();
400            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
401            if let Some(f) = (*parent_class).set_client_widget {
402                f(
403                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
404                    widget.map(|p| p.as_ref()).to_glib_none().0,
405                )
406            }
407        }
408    }
409
410    fn parent_set_cursor_location(&self, area: &gdk::Rectangle) {
411        unsafe {
412            let data = Self::type_data();
413            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
414            if let Some(f) = (*parent_class).set_cursor_location {
415                f(
416                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
417                    area.to_glib_none().0 as *mut _,
418                );
419            }
420        }
421    }
422
423    #[cfg_attr(feature = "v4_2", deprecated = "Since 4.2")]
424    #[allow(deprecated)]
425    fn parent_set_surrounding(&self, text: &str, cursor_index: i32) {
426        unsafe {
427            let data = Self::type_data();
428            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
429            if let Some(f) = (*parent_class).set_surrounding {
430                f(
431                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
432                    text.to_glib_none().0,
433                    text.len() as i32,
434                    cursor_index,
435                )
436            }
437        }
438    }
439
440    fn parent_set_use_preedit(&self, use_preedit: bool) {
441        unsafe {
442            let data = Self::type_data();
443            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
444            if let Some(f) = (*parent_class).set_use_preedit {
445                f(
446                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
447                    use_preedit.into_glib(),
448                )
449            }
450        }
451    }
452
453    #[cfg(feature = "v4_10")]
454    #[cfg_attr(docsrs, doc(cfg(feature = "v4_10")))]
455    fn parent_activate_osk(&self) {
456        unsafe {
457            let data = Self::type_data();
458            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
459            if let Some(f) = (*parent_class).activate_osk {
460                f(self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0)
461            }
462        }
463    }
464
465    #[cfg(feature = "v4_14")]
466    #[cfg_attr(docsrs, doc(cfg(feature = "v4_14")))]
467    fn parent_activate_osk_with_event(&self, event: Option<&gdk::Event>) -> bool {
468        unsafe {
469            let data = Self::type_data();
470            let parent_class = data.as_ref().parent_class() as *mut ffi::GtkIMContextClass;
471            if let Some(f) = (*parent_class).activate_osk_with_event {
472                from_glib(f(
473                    self.obj().unsafe_cast_ref::<IMContext>().to_glib_none().0,
474                    event.to_glib_none().0,
475                ))
476            } else {
477                false
478            }
479        }
480    }
481}
482
483impl<T: IMContextImpl> IMContextImplExt for T {}
484
485unsafe impl<T: IMContextImpl> IsSubclassable<T> for IMContext {
486    fn class_init(class: &mut glib::Class<Self>) {
487        Self::parent_class_init::<T>(class);
488
489        assert_initialized_main_thread!();
490
491        let klass = class.as_mut();
492        klass.commit = Some(im_context_commit::<T>);
493        klass.delete_surrounding = Some(im_context_delete_surrounding::<T>);
494        klass.filter_keypress = Some(im_context_filter_keypress::<T>);
495        klass.focus_in = Some(im_context_focus_in::<T>);
496        klass.focus_out = Some(im_context_focus_out::<T>);
497        klass.get_preedit_string = Some(im_context_get_preedit_string::<T>);
498        klass.get_surrounding = Some(im_context_get_surrounding::<T>);
499        klass.preedit_changed = Some(im_context_preedit_changed::<T>);
500        klass.preedit_end = Some(im_context_preedit_end::<T>);
501        klass.preedit_start = Some(im_context_preedit_start::<T>);
502        klass.reset = Some(im_context_reset::<T>);
503        klass.retrieve_surrounding = Some(im_context_retrieve_surrounding::<T>);
504        klass.set_client_widget = Some(im_context_set_client_widget::<T>);
505        klass.set_cursor_location = Some(im_context_set_cursor_location::<T>);
506        klass.set_surrounding = Some(im_context_set_surrounding::<T>);
507        klass.set_use_preedit = Some(im_context_set_use_preedit::<T>);
508        #[cfg(feature = "v4_10")]
509        #[cfg_attr(docsrs, doc(cfg(feature = "v4_10")))]
510        {
511            klass.activate_osk = Some(im_context_activate_osk::<T>);
512        };
513        #[cfg(feature = "v4_14")]
514        #[cfg_attr(docsrs, doc(cfg(feature = "v4_14")))]
515        {
516            klass.activate_osk_with_event = Some(im_context_activate_osk_with_event::<T>);
517        };
518    }
519}
520
521unsafe extern "C" fn im_context_commit<T: IMContextImpl>(
522    ptr: *mut ffi::GtkIMContext,
523    stringptr: *const libc::c_char,
524) {
525    let instance = &*(ptr as *mut T::Instance);
526    let imp = instance.imp();
527    let string: Borrowed<GString> = from_glib_borrow(stringptr);
528
529    imp.commit(string.as_str())
530}
531
532unsafe extern "C" fn im_context_delete_surrounding<T: IMContextImpl>(
533    ptr: *mut ffi::GtkIMContext,
534    offset: i32,
535    n_chars: i32,
536) -> glib::ffi::gboolean {
537    let instance = &*(ptr as *mut T::Instance);
538    let imp = instance.imp();
539
540    imp.delete_surrounding(offset, n_chars).into_glib()
541}
542
543unsafe extern "C" fn im_context_filter_keypress<T: IMContextImpl>(
544    ptr: *mut ffi::GtkIMContext,
545    eventptr: *mut gdk::ffi::GdkEvent,
546) -> glib::ffi::gboolean {
547    let instance = &*(ptr as *mut T::Instance);
548    let imp = instance.imp();
549    let event: Borrowed<gdk::Event> = from_glib_borrow(eventptr);
550    imp.filter_keypress(&event).into_glib()
551}
552
553unsafe extern "C" fn im_context_focus_in<T: IMContextImpl>(ptr: *mut ffi::GtkIMContext) {
554    let instance = &*(ptr as *mut T::Instance);
555    let imp = instance.imp();
556
557    imp.focus_in()
558}
559
560unsafe extern "C" fn im_context_focus_out<T: IMContextImpl>(ptr: *mut ffi::GtkIMContext) {
561    let instance = &*(ptr as *mut T::Instance);
562    let imp = instance.imp();
563
564    imp.focus_out()
565}
566
567unsafe extern "C" fn im_context_get_preedit_string<T: IMContextImpl>(
568    ptr: *mut ffi::GtkIMContext,
569    text_ptr: *mut *mut libc::c_char,
570    attrs_ptr: *mut *mut pango::ffi::PangoAttrList,
571    cursor_index_ptr: *mut libc::c_int,
572) {
573    let instance = &*(ptr as *mut T::Instance);
574    let imp = instance.imp();
575
576    let (text, attrs, cursor_idx) = imp.preedit_string();
577
578    *text_ptr = text.into_glib_ptr();
579    *cursor_index_ptr = cursor_idx;
580    *attrs_ptr = attrs.into_glib_ptr();
581}
582
583unsafe extern "C" fn im_context_get_surrounding<T: IMContextImpl>(
584    ptr: *mut ffi::GtkIMContext,
585    text_ptr: *mut *mut libc::c_char,
586    cursor_index_ptr: *mut libc::c_int,
587) -> glib::ffi::gboolean {
588    let instance = &*(ptr as *mut T::Instance);
589    let imp = instance.imp();
590
591    if let Some((text, cursor_idx)) = imp.surrounding() {
592        *text_ptr = text.into_glib_ptr();
593        *cursor_index_ptr = cursor_idx;
594        true.into_glib()
595    } else {
596        *text_ptr = std::ptr::null_mut();
597        *cursor_index_ptr = 0;
598        false.into_glib()
599    }
600}
601
602unsafe extern "C" fn im_context_preedit_changed<T: IMContextImpl>(ptr: *mut ffi::GtkIMContext) {
603    let instance = &*(ptr as *mut T::Instance);
604    let imp = instance.imp();
605
606    imp.preedit_changed()
607}
608
609unsafe extern "C" fn im_context_preedit_end<T: IMContextImpl>(ptr: *mut ffi::GtkIMContext) {
610    let instance = &*(ptr as *mut T::Instance);
611    let imp = instance.imp();
612
613    imp.preedit_end()
614}
615
616unsafe extern "C" fn im_context_preedit_start<T: IMContextImpl>(ptr: *mut ffi::GtkIMContext) {
617    let instance = &*(ptr as *mut T::Instance);
618    let imp = instance.imp();
619
620    imp.preedit_start()
621}
622
623unsafe extern "C" fn im_context_reset<T: IMContextImpl>(ptr: *mut ffi::GtkIMContext) {
624    let instance = &*(ptr as *mut T::Instance);
625    let imp = instance.imp();
626
627    imp.reset()
628}
629
630unsafe extern "C" fn im_context_retrieve_surrounding<T: IMContextImpl>(
631    ptr: *mut ffi::GtkIMContext,
632) -> glib::ffi::gboolean {
633    let instance = &*(ptr as *mut T::Instance);
634    let imp = instance.imp();
635
636    imp.retrieve_surrounding().into_glib()
637}
638
639unsafe extern "C" fn im_context_set_client_widget<T: IMContextImpl>(
640    ptr: *mut ffi::GtkIMContext,
641    widgetptr: *mut ffi::GtkWidget,
642) {
643    let instance = &*(ptr as *mut T::Instance);
644    let imp = instance.imp();
645    let widget: Borrowed<Option<Widget>> = from_glib_borrow(widgetptr);
646
647    imp.set_client_widget(widget.as_ref().as_ref());
648}
649
650unsafe extern "C" fn im_context_set_cursor_location<T: IMContextImpl>(
651    ptr: *mut ffi::GtkIMContext,
652    areaptr: *mut gdk::ffi::GdkRectangle,
653) {
654    let instance = &*(ptr as *mut T::Instance);
655    let imp = instance.imp();
656    let area = from_glib_borrow(areaptr);
657
658    imp.set_cursor_location(&area);
659}
660
661unsafe extern "C" fn im_context_set_surrounding<T: IMContextImpl>(
662    ptr: *mut ffi::GtkIMContext,
663    textptr: *const libc::c_char,
664    length: i32,
665    cursor_index: i32,
666) {
667    let instance = &*(ptr as *mut T::Instance);
668    let imp = instance.imp();
669    let text: Borrowed<GString> = from_glib_borrow(textptr);
670
671    // length == -1 if text is null-terminated
672    let text = if length == -1 {
673        &text[..]
674    } else {
675        &text[0..(length as usize)]
676    };
677
678    imp.set_surrounding(text, cursor_index)
679}
680
681unsafe extern "C" fn im_context_set_use_preedit<T: IMContextImpl>(
682    ptr: *mut ffi::GtkIMContext,
683    use_preedit: glib::ffi::gboolean,
684) {
685    let instance = &*(ptr as *mut T::Instance);
686    let imp = instance.imp();
687
688    imp.set_use_preedit(from_glib(use_preedit))
689}
690
691#[cfg(feature = "v4_10")]
692#[cfg_attr(docsrs, doc(cfg(feature = "v4_10")))]
693unsafe extern "C" fn im_context_activate_osk<T: IMContextImpl>(ptr: *mut ffi::GtkIMContext) {
694    let instance = &*(ptr as *mut T::Instance);
695    let imp = instance.imp();
696
697    imp.activate_osk()
698}
699
700#[cfg(feature = "v4_14")]
701#[cfg_attr(docsrs, doc(cfg(feature = "v4_14")))]
702unsafe extern "C" fn im_context_activate_osk_with_event<T: IMContextImpl>(
703    ptr: *mut ffi::GtkIMContext,
704    eventptr: *mut gdk::ffi::GdkEvent,
705) -> glib::ffi::gboolean {
706    let instance = &*(ptr as *mut T::Instance);
707    let imp = instance.imp();
708
709    let event: Borrowed<Option<gdk::Event>> = from_glib_borrow(eventptr);
710
711    imp.activate_osk_with_event(event.as_ref().as_ref())
712        .into_glib()
713}