Backport official GLib iterator safety fix for Linux desktop
This commit is contained in:
1 parent
43c43ebdc9
commit
8ef874c609
133 files changed
+55342
-2
No files matched your search
@@ -0,0 +1,59 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, BindingFlags};
|
||||
use std::fmt;
|
||||
|
||||
crate::wrapper! {
|
||||
#[doc(alias = "GBinding")]
|
||||
pub struct Binding(Object<gobject_ffi::GBinding>);
|
||||
|
||||
match fn {
|
||||
type_ => || gobject_ffi::g_binding_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Binding {
|
||||
#[doc(alias = "g_binding_get_flags")]
|
||||
#[doc(alias = "get_flags")]
|
||||
pub fn flags(&self) -> BindingFlags {
|
||||
unsafe { from_glib(gobject_ffi::g_binding_get_flags(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_get_source_property")]
|
||||
#[doc(alias = "get_source_property")]
|
||||
pub fn source_property(&self) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(gobject_ffi::g_binding_get_source_property(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_get_target_property")]
|
||||
#[doc(alias = "get_target_property")]
|
||||
pub fn target_property(&self) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(gobject_ffi::g_binding_get_target_property(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_unbind")]
|
||||
pub fn unbind(&self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_binding_unbind(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Binding {}
|
||||
unsafe impl Sync for Binding {}
|
||||
|
||||
impl fmt::Display for Binding {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("Binding")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
signal::{connect_raw, SignalHandlerId},
|
||||
translate::*,
|
||||
Object,
|
||||
};
|
||||
use std::{boxed::Box as Box_, fmt, mem::transmute};
|
||||
|
||||
crate::wrapper! {
|
||||
#[doc(alias = "GBindingGroup")]
|
||||
pub struct BindingGroup(Object<gobject_ffi::GBindingGroup>);
|
||||
|
||||
match fn {
|
||||
type_ => || gobject_ffi::g_binding_group_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl BindingGroup {
|
||||
#[doc(alias = "g_binding_group_new")]
|
||||
pub fn new() -> BindingGroup {
|
||||
unsafe { from_glib_full(gobject_ffi::g_binding_group_new()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_group_dup_source")]
|
||||
#[doc(alias = "dup_source")]
|
||||
pub fn source(&self) -> Option<Object> {
|
||||
unsafe {
|
||||
from_glib_none(gobject_ffi::g_binding_group_dup_source(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_group_set_source")]
|
||||
pub fn set_source(&self, source: Option<&impl IsA<Object>>) {
|
||||
unsafe {
|
||||
gobject_ffi::g_binding_group_set_source(
|
||||
self.to_glib_none().0,
|
||||
source.map(|p| p.as_ref()).to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[doc(alias = "source")]
|
||||
pub fn connect_source_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn notify_source_trampoline<
|
||||
F: Fn(&BindingGroup) + Send + Sync + 'static,
|
||||
>(
|
||||
this: *mut crate::gobject_ffi::GBindingGroup,
|
||||
_param_spec: ffi::gpointer,
|
||||
f: ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this))
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"notify::source\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
notify_source_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
impl Default for BindingGroup {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for BindingGroup {}
|
||||
unsafe impl Sync for BindingGroup {}
|
||||
|
||||
impl fmt::Display for BindingGroup {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("BindingGroup")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{bitflags::bitflags, prelude::*, translate::*};
|
||||
use std::fmt;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GBindingFlags")]
|
||||
pub struct BindingFlags: u32 {
|
||||
#[doc(alias = "G_BINDING_DEFAULT")]
|
||||
const DEFAULT = gobject_ffi::G_BINDING_DEFAULT as _;
|
||||
#[doc(alias = "G_BINDING_BIDIRECTIONAL")]
|
||||
const BIDIRECTIONAL = gobject_ffi::G_BINDING_BIDIRECTIONAL as _;
|
||||
#[doc(alias = "G_BINDING_SYNC_CREATE")]
|
||||
const SYNC_CREATE = gobject_ffi::G_BINDING_SYNC_CREATE as _;
|
||||
#[doc(alias = "G_BINDING_INVERT_BOOLEAN")]
|
||||
const INVERT_BOOLEAN = gobject_ffi::G_BINDING_INVERT_BOOLEAN as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BindingFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for BindingFlags {
|
||||
type GlibType = gobject_ffi::GBindingFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> gobject_ffi::GBindingFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<gobject_ffi::GBindingFlags> for BindingFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: gobject_ffi::GBindingFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticType for BindingFlags {
|
||||
#[inline]
|
||||
#[doc(alias = "g_binding_flags_get_type")]
|
||||
fn static_type() -> crate::Type {
|
||||
unsafe { from_glib(gobject_ffi::g_binding_flags_get_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::HasParamSpec for BindingFlags {
|
||||
type ParamSpec = crate::ParamSpecFlags;
|
||||
type SetValue = Self;
|
||||
type BuilderFn = fn(&str) -> crate::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::value::ValueType for BindingFlags {
|
||||
type Type = Self;
|
||||
}
|
||||
|
||||
unsafe impl<'a> crate::value::FromValue<'a> for BindingFlags {
|
||||
type Checker = crate::value::GenericValueTypeChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a crate::Value) -> Self {
|
||||
from_glib(crate::gobject_ffi::g_value_get_flags(
|
||||
value.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for BindingFlags {
|
||||
#[inline]
|
||||
fn to_value(&self) -> crate::Value {
|
||||
let mut value = crate::Value::for_value_type::<Self>();
|
||||
unsafe {
|
||||
crate::gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, self.into_glib());
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&self) -> crate::Type {
|
||||
Self::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BindingFlags> for crate::Value {
|
||||
#[inline]
|
||||
fn from(v: BindingFlags) -> Self {
|
||||
ToValue::to_value(&v)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GSignalFlags")]
|
||||
pub struct SignalFlags: u32 {
|
||||
#[doc(alias = "G_SIGNAL_RUN_FIRST")]
|
||||
const RUN_FIRST = gobject_ffi::G_SIGNAL_RUN_FIRST as _;
|
||||
#[doc(alias = "G_SIGNAL_RUN_LAST")]
|
||||
const RUN_LAST = gobject_ffi::G_SIGNAL_RUN_LAST as _;
|
||||
#[doc(alias = "G_SIGNAL_RUN_CLEANUP")]
|
||||
const RUN_CLEANUP = gobject_ffi::G_SIGNAL_RUN_CLEANUP as _;
|
||||
#[doc(alias = "G_SIGNAL_NO_RECURSE")]
|
||||
const NO_RECURSE = gobject_ffi::G_SIGNAL_NO_RECURSE as _;
|
||||
#[doc(alias = "G_SIGNAL_DETAILED")]
|
||||
const DETAILED = gobject_ffi::G_SIGNAL_DETAILED as _;
|
||||
#[doc(alias = "G_SIGNAL_ACTION")]
|
||||
const ACTION = gobject_ffi::G_SIGNAL_ACTION as _;
|
||||
#[doc(alias = "G_SIGNAL_NO_HOOKS")]
|
||||
const NO_HOOKS = gobject_ffi::G_SIGNAL_NO_HOOKS as _;
|
||||
#[doc(alias = "G_SIGNAL_MUST_COLLECT")]
|
||||
const MUST_COLLECT = gobject_ffi::G_SIGNAL_MUST_COLLECT as _;
|
||||
#[doc(alias = "G_SIGNAL_DEPRECATED")]
|
||||
const DEPRECATED = gobject_ffi::G_SIGNAL_DEPRECATED as _;
|
||||
#[doc(alias = "G_SIGNAL_ACCUMULATOR_FIRST_RUN")]
|
||||
const ACCUMULATOR_FIRST_RUN = gobject_ffi::G_SIGNAL_ACCUMULATOR_FIRST_RUN as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SignalFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for SignalFlags {
|
||||
type GlibType = gobject_ffi::GSignalFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> gobject_ffi::GSignalFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<gobject_ffi::GSignalFlags> for SignalFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: gobject_ffi::GSignalFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
mod binding;
|
||||
pub use self::binding::Binding;
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
mod binding_group;
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
pub use self::binding_group::BindingGroup;
|
||||
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
mod signal_group;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
pub use self::signal_group::SignalGroup;
|
||||
|
||||
mod flags;
|
||||
pub use self::flags::BindingFlags;
|
||||
pub use self::flags::SignalFlags;
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{prelude::*, translate::*, Object};
|
||||
use std::fmt;
|
||||
|
||||
crate::wrapper! {
|
||||
#[doc(alias = "GSignalGroup")]
|
||||
pub struct SignalGroup(Object<gobject_ffi::GSignalGroup>);
|
||||
|
||||
match fn {
|
||||
type_ => || gobject_ffi::g_signal_group_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl SignalGroup {
|
||||
#[doc(alias = "g_signal_group_new")]
|
||||
#[doc(alias = "new")]
|
||||
pub fn with_type(target_type: crate::types::Type) -> SignalGroup {
|
||||
unsafe { from_glib_full(gobject_ffi::g_signal_group_new(target_type.into_glib())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_block")]
|
||||
pub fn block(&self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_group_block(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_dup_target")]
|
||||
#[doc(alias = "dup_target")]
|
||||
pub fn target(&self) -> Option<Object> {
|
||||
unsafe {
|
||||
from_glib_full(gobject_ffi::g_signal_group_dup_target(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_set_target")]
|
||||
pub fn set_target(&self, target: Option<&impl IsA<Object>>) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_group_set_target(
|
||||
self.to_glib_none().0,
|
||||
target.map(|p| p.as_ref()).to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_unblock")]
|
||||
pub fn unblock(&self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_group_unblock(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[doc(alias = "target-type")]
|
||||
pub fn target_type(&self) -> crate::types::Type {
|
||||
ObjectExt::property(self, "target-type")
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for SignalGroup {}
|
||||
unsafe impl Sync for SignalGroup {}
|
||||
|
||||
impl fmt::Display for SignalGroup {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("SignalGroup")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::{prelude::*, Binding, Object};
|
||||
|
||||
impl Binding {
|
||||
#[doc(alias = "get_source")]
|
||||
pub fn source(&self) -> Option<Object> {
|
||||
self.property("source")
|
||||
}
|
||||
|
||||
#[doc(alias = "get_target")]
|
||||
pub fn target(&self) -> Option<Object> {
|
||||
self.property("target")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{prelude::*, subclass::prelude::*};
|
||||
|
||||
#[test]
|
||||
fn binding() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
assert!(source.find_property("name").is_some());
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.bidirectional()
|
||||
.build();
|
||||
|
||||
source.set_name("test_source_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
|
||||
target.set_name("test_target_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_with_values() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.transform_to_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.transform_from_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert_eq!(target.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform_with_values() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.transform_from_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
target.set_name("Hello");
|
||||
assert_eq!(source.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_ref() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert_eq!(target.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_owned_ref() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value: String| Some(format!("{value} World")))
|
||||
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert_eq!(target.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.build();
|
||||
|
||||
target.set_name("Hello");
|
||||
assert_eq!(source.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_with_values_change_type() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.transform_to_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some((value == "Hello").to_value())
|
||||
})
|
||||
.transform_from_with_values(|_binding, value| {
|
||||
let value = value.get::<bool>().unwrap();
|
||||
Some((if value { "Hello" } else { "World" }).to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert!(target.enabled());
|
||||
|
||||
source.set_name("Hello World");
|
||||
assert!(!target.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform_values_change_type() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some((value == "Hello").to_value())
|
||||
})
|
||||
.transform_from_with_values(|_binding, value| {
|
||||
let value = value.get::<bool>().unwrap();
|
||||
Some((if value { "Hello" } else { "World" }).to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
target.set_enabled(true);
|
||||
assert_eq!(source.name(), "Hello");
|
||||
target.set_enabled(false);
|
||||
assert_eq!(source.name(), "World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_change_type() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value: &str| Some(value == "Hello"))
|
||||
.transform_from(|_binding, value: bool| Some(if value { "Hello" } else { "World" }))
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert!(target.enabled());
|
||||
|
||||
source.set_name("Hello World");
|
||||
assert!(!target.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform_change_type() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to(|_binding, value: &str| Some(value == "Hello"))
|
||||
.transform_from(|_binding, value: bool| Some(if value { "Hello" } else { "World" }))
|
||||
.build();
|
||||
|
||||
target.set_enabled(true);
|
||||
assert_eq!(source.name(), "Hello");
|
||||
target.set_enabled(false);
|
||||
assert_eq!(source.name(), "World");
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
use crate as glib;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TestObject {
|
||||
pub name: RefCell<String>,
|
||||
pub enabled: RefCell<bool>,
|
||||
}
|
||||
|
||||
#[crate::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestBinding";
|
||||
type Type = super::TestObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {
|
||||
fn properties() -> &'static [crate::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<crate::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
crate::ParamSpecString::builder("name")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
crate::ParamSpecBoolean::builder("enabled")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> crate::Value {
|
||||
let obj = self.obj();
|
||||
match pspec.name() {
|
||||
"name" => obj.name().to_value(),
|
||||
"enabled" => obj.enabled().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &crate::Value, pspec: &crate::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
match pspec.name() {
|
||||
"name" => obj.set_name(value.get().unwrap()),
|
||||
"enabled" => obj.set_enabled(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp::TestObject>);
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
crate::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestObject {
|
||||
fn name(&self) -> String {
|
||||
self.imp().name.borrow().clone()
|
||||
}
|
||||
|
||||
fn set_name(&self, name: &str) {
|
||||
if name != self.imp().name.replace(name.to_string()).as_str() {
|
||||
self.notify("name");
|
||||
}
|
||||
}
|
||||
|
||||
fn enabled(&self) -> bool {
|
||||
*self.imp().enabled.borrow()
|
||||
}
|
||||
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
if enabled != self.imp().enabled.replace(enabled) {
|
||||
self.notify("enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{fmt, ptr};
|
||||
|
||||
use crate::{
|
||||
object::ObjectRef, prelude::*, translate::*, Binding, BindingFlags, BindingGroup, BoolError,
|
||||
Object, ParamSpec, Value,
|
||||
};
|
||||
|
||||
impl BindingGroup {
|
||||
#[doc(alias = "bind_with_closures")]
|
||||
pub fn bind<'a, O: ObjectType>(
|
||||
&'a self,
|
||||
source_property: &'a str,
|
||||
target: &'a O,
|
||||
target_property: &'a str,
|
||||
) -> BindingGroupBuilder<'a> {
|
||||
BindingGroupBuilder::new(self, source_property, target, target_property)
|
||||
}
|
||||
}
|
||||
|
||||
type TransformFn = Option<Box<dyn Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>>;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builder for binding group bindings.
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct BindingGroupBuilder<'a> {
|
||||
group: &'a BindingGroup,
|
||||
source_property: &'a str,
|
||||
target: &'a ObjectRef,
|
||||
target_property: &'a str,
|
||||
flags: BindingFlags,
|
||||
transform_to: TransformFn,
|
||||
transform_from: TransformFn,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for BindingGroupBuilder<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("BindingGroupBuilder")
|
||||
.field("group", &self.group)
|
||||
.field("source_property", &self.source_property)
|
||||
.field("target", &self.target)
|
||||
.field("target_property", &self.target_property)
|
||||
.field("flags", &self.flags)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BindingGroupBuilder<'a> {
|
||||
fn new(
|
||||
group: &'a BindingGroup,
|
||||
source_property: &'a str,
|
||||
target: &'a impl ObjectType,
|
||||
target_property: &'a str,
|
||||
) -> Self {
|
||||
Self {
|
||||
group,
|
||||
source_property,
|
||||
target: target.as_object_ref(),
|
||||
target_property,
|
||||
flags: BindingFlags::DEFAULT,
|
||||
transform_to: None,
|
||||
transform_from: None,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Transform changed property values from the target object to the source object with the given closure.
|
||||
pub fn transform_from<F: Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>(
|
||||
self,
|
||||
func: F,
|
||||
) -> Self {
|
||||
Self {
|
||||
transform_from: Some(Box::new(func)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Transform changed property values from the source object to the target object with the given closure.
|
||||
pub fn transform_to<F: Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>(
|
||||
self,
|
||||
func: F,
|
||||
) -> Self {
|
||||
Self {
|
||||
transform_to: Some(Box::new(func)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Bind the properties with the given flags.
|
||||
pub fn flags(self, flags: BindingFlags) -> Self {
|
||||
Self { flags, ..self }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set the binding flags to [`BIDIRECTIONAL`][crate::BindingFlags::BIDIRECTIONAL].
|
||||
pub fn bidirectional(mut self) -> Self {
|
||||
self.flags |= crate::BindingFlags::BIDIRECTIONAL;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set the binding flags to [`SYNC_CREATE`][crate::BindingFlags::SYNC_CREATE].
|
||||
pub fn sync_create(mut self) -> Self {
|
||||
self.flags |= crate::BindingFlags::SYNC_CREATE;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set the binding flags to [`INVERT_BOOLEAN`][crate::BindingFlags::INVERT_BOOLEAN].
|
||||
pub fn invert_boolean(mut self) -> Self {
|
||||
self.flags |= crate::BindingFlags::INVERT_BOOLEAN;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Establish the property binding.
|
||||
///
|
||||
/// This fails if the provided properties do not exist.
|
||||
pub fn try_build(self) -> Result<(), BoolError> {
|
||||
unsafe extern "C" fn transform_to_trampoline(
|
||||
binding: *mut gobject_ffi::GBinding,
|
||||
from_value: *const gobject_ffi::GValue,
|
||||
to_value: *mut gobject_ffi::GValue,
|
||||
user_data: ffi::gpointer,
|
||||
) -> ffi::gboolean {
|
||||
let transform_data =
|
||||
&*(user_data as *const (TransformFn, TransformFn, String, ParamSpec));
|
||||
|
||||
match (transform_data.0.as_ref().unwrap())(
|
||||
&from_glib_borrow(binding),
|
||||
&*(from_value as *const Value),
|
||||
) {
|
||||
None => false,
|
||||
Some(res) => {
|
||||
assert!(
|
||||
res.type_().is_a(transform_data.3.value_type()),
|
||||
"Target property {} expected type {} but transform_to function returned {}",
|
||||
transform_data.3.name(),
|
||||
transform_data.3.value_type(),
|
||||
res.type_()
|
||||
);
|
||||
*to_value = res.into_raw();
|
||||
true
|
||||
}
|
||||
}
|
||||
.into_glib()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn transform_from_trampoline(
|
||||
binding: *mut gobject_ffi::GBinding,
|
||||
from_value: *const gobject_ffi::GValue,
|
||||
to_value: *mut gobject_ffi::GValue,
|
||||
user_data: ffi::gpointer,
|
||||
) -> ffi::gboolean {
|
||||
let transform_data =
|
||||
&*(user_data as *const (TransformFn, TransformFn, String, ParamSpec));
|
||||
let binding = from_glib_borrow(binding);
|
||||
|
||||
match (transform_data.1.as_ref().unwrap())(
|
||||
&binding,
|
||||
&*(from_value as *const Value),
|
||||
) {
|
||||
None => false,
|
||||
Some(res) => {
|
||||
let pspec_name = transform_data.2.clone();
|
||||
let source = binding.source().unwrap();
|
||||
let pspec = source.find_property(&pspec_name);
|
||||
assert!(pspec.is_some(), "Source object does not have a property {pspec_name}");
|
||||
let pspec = pspec.unwrap();
|
||||
|
||||
assert!(
|
||||
res.type_().is_a(pspec.value_type()),
|
||||
"Source property {pspec_name} expected type {} but transform_from function returned {}",
|
||||
pspec.value_type(),
|
||||
res.type_()
|
||||
);
|
||||
*to_value = res.into_raw();
|
||||
true
|
||||
}
|
||||
}
|
||||
.into_glib()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn free_transform_data(data: ffi::gpointer) {
|
||||
let _ = Box::from_raw(data as *mut (TransformFn, TransformFn, String, ParamSpec));
|
||||
}
|
||||
|
||||
let mut _source_propery_name_cstr = None;
|
||||
let source_property_name = if let Some(source) = self.group.source() {
|
||||
let source_property = source.find_property(self.source_property).ok_or_else(|| {
|
||||
bool_error!(
|
||||
"Source property {} on type {} not found",
|
||||
self.source_property,
|
||||
source.type_()
|
||||
)
|
||||
})?;
|
||||
|
||||
// This is NUL-termianted from the C side
|
||||
source_property.name().as_ptr()
|
||||
} else {
|
||||
// This is a Rust &str and needs to be NUL-terminated first
|
||||
let source_propery_name = std::ffi::CString::new(self.source_property).unwrap();
|
||||
let source_property_name_ptr = source_propery_name.as_ptr() as *const u8;
|
||||
_source_propery_name_cstr = Some(source_propery_name);
|
||||
|
||||
source_property_name_ptr
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let target: Object = from_glib_none(self.target.clone().to_glib_none().0);
|
||||
|
||||
let target_property = target.find_property(self.target_property).ok_or_else(|| {
|
||||
bool_error!(
|
||||
"Target property {} on type {} not found",
|
||||
self.target_property,
|
||||
target.type_()
|
||||
)
|
||||
})?;
|
||||
|
||||
let target_property_name = target_property.name().as_ptr();
|
||||
|
||||
let have_transform_to = self.transform_to.is_some();
|
||||
let have_transform_from = self.transform_from.is_some();
|
||||
let transform_data = if have_transform_to || have_transform_from {
|
||||
Box::into_raw(Box::new((
|
||||
self.transform_to,
|
||||
self.transform_from,
|
||||
String::from_glib_none(source_property_name as *const _),
|
||||
target_property,
|
||||
)))
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
};
|
||||
|
||||
gobject_ffi::g_binding_group_bind_full(
|
||||
self.group.to_glib_none().0,
|
||||
source_property_name as *const _,
|
||||
target.to_glib_none().0,
|
||||
target_property_name as *const _,
|
||||
self.flags.into_glib(),
|
||||
if have_transform_to {
|
||||
Some(transform_to_trampoline)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if have_transform_from {
|
||||
Some(transform_from_trampoline)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
transform_data as ffi::gpointer,
|
||||
if transform_data.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(free_transform_data)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Similar to `try_build` but panics instead of failing.
|
||||
pub fn build(self) {
|
||||
self.try_build().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{prelude::*, subclass::prelude::*};
|
||||
|
||||
#[test]
|
||||
fn binding_without_source() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
assert!(source.find_property("name").is_some());
|
||||
binding_group
|
||||
.bind("name", &target, "name")
|
||||
.bidirectional()
|
||||
.build();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
|
||||
source.set_name("test_source_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
|
||||
target.set_name("test_target_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_with_source() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
|
||||
binding_group.bind("name", &target, "name").build();
|
||||
|
||||
source.set_name("test_source_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
binding_group
|
||||
.bind("name", &target, "name")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.transform_from(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert_eq!(target.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
binding_group
|
||||
.bind("name", &target, "name")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.transform_from(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
target.set_name("Hello");
|
||||
assert_eq!(source.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_change_type() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
binding_group
|
||||
.bind("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some((value == "Hello").to_value())
|
||||
})
|
||||
.transform_from(|_binding, value| {
|
||||
let value = value.get::<bool>().unwrap();
|
||||
Some((if value { "Hello" } else { "World" }).to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert!(target.enabled());
|
||||
|
||||
source.set_name("Hello World");
|
||||
assert!(!target.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform_change_type() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
binding_group
|
||||
.bind("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some((value == "Hello").to_value())
|
||||
})
|
||||
.transform_from(|_binding, value| {
|
||||
let value = value.get::<bool>().unwrap();
|
||||
Some((if value { "Hello" } else { "World" }).to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
target.set_enabled(true);
|
||||
assert_eq!(source.name(), "Hello");
|
||||
target.set_enabled(false);
|
||||
assert_eq!(source.name(), "World");
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
use crate as glib;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TestObject {
|
||||
pub name: RefCell<String>,
|
||||
pub enabled: RefCell<bool>,
|
||||
}
|
||||
|
||||
#[crate::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestBindingGroup";
|
||||
type Type = super::TestObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {
|
||||
fn properties() -> &'static [crate::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<crate::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
crate::ParamSpecString::builder("name")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
crate::ParamSpecBoolean::builder("enabled")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> crate::Value {
|
||||
let obj = self.obj();
|
||||
match pspec.name() {
|
||||
"name" => obj.name().to_value(),
|
||||
"enabled" => obj.enabled().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &crate::Value, pspec: &crate::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
match pspec.name() {
|
||||
"name" => obj.set_name(value.get().unwrap()),
|
||||
"enabled" => obj.set_enabled(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp::TestObject>);
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
crate::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestObject {
|
||||
fn name(&self) -> String {
|
||||
self.imp().name.borrow().clone()
|
||||
}
|
||||
|
||||
fn set_name(&self, name: &str) {
|
||||
if name != self.imp().name.replace(name.to_string()).as_str() {
|
||||
self.notify("name");
|
||||
}
|
||||
}
|
||||
|
||||
fn enabled(&self) -> bool {
|
||||
*self.imp().enabled.borrow()
|
||||
}
|
||||
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
if enabled != self.imp().enabled.replace(enabled) {
|
||||
self.notify("enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[doc(alias = "GParamFlags")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ParamFlags: u32 {
|
||||
#[doc(alias = "G_PARAM_READABLE")]
|
||||
const READABLE = gobject_ffi::G_PARAM_READABLE as _;
|
||||
#[doc(alias = "G_PARAM_WRITABLE")]
|
||||
const WRITABLE = gobject_ffi::G_PARAM_WRITABLE as _;
|
||||
#[doc(alias = "G_PARAM_READWRITE")]
|
||||
const READWRITE = gobject_ffi::G_PARAM_READWRITE as _;
|
||||
#[doc(alias = "G_PARAM_CONSTRUCT")]
|
||||
const CONSTRUCT = gobject_ffi::G_PARAM_CONSTRUCT as _;
|
||||
#[doc(alias = "G_PARAM_CONSTRUCT_ONLY")]
|
||||
const CONSTRUCT_ONLY = gobject_ffi::G_PARAM_CONSTRUCT_ONLY as _;
|
||||
#[doc(alias = "G_PARAM_LAX_VALIDATION")]
|
||||
const LAX_VALIDATION = gobject_ffi::G_PARAM_LAX_VALIDATION as _;
|
||||
const USER_0 = 256;
|
||||
const USER_1 = 512;
|
||||
const USER_2 = 1024;
|
||||
const USER_3 = 2048;
|
||||
const USER_4 = 4096;
|
||||
const USER_5 = 8192;
|
||||
const USER_6 = 16384;
|
||||
const USER_7 = 32768;
|
||||
const USER_8 = 65536;
|
||||
#[doc(alias = "G_PARAM_EXPLICIT_NOTIFY")]
|
||||
const EXPLICIT_NOTIFY = gobject_ffi::G_PARAM_EXPLICIT_NOTIFY as _;
|
||||
#[doc(alias = "G_PARAM_DEPRECATED")]
|
||||
const DEPRECATED = gobject_ffi::G_PARAM_DEPRECATED as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ParamFlags {
|
||||
fn default() -> Self {
|
||||
ParamFlags::READWRITE
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for ParamFlags {
|
||||
type GlibType = gobject_ffi::GParamFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> gobject_ffi::GParamFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<gobject_ffi::GParamFlags> for ParamFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: gobject_ffi::GParamFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! GObject bindings
|
||||
|
||||
#[allow(unused_imports)]
|
||||
mod auto;
|
||||
mod binding;
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
mod binding_group;
|
||||
mod flags;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
mod signal_group;
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
pub use binding_group::BindingGroupBuilder;
|
||||
|
||||
pub use self::{auto::*, flags::*};
|
||||
//pub use self::auto::functions::*;
|
||||
@@ -0,0 +1,248 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::mem::transmute;
|
||||
|
||||
use crate::{
|
||||
signal::{connect_raw, SignalHandlerId},
|
||||
translate::*,
|
||||
IsA, Object, ObjectType, RustClosure, SignalGroup, Value,
|
||||
};
|
||||
|
||||
impl SignalGroup {
|
||||
#[doc(alias = "g_signal_group_new")]
|
||||
pub fn new<T: IsA<Object>>() -> Self {
|
||||
Self::with_type(T::static_type())
|
||||
}
|
||||
#[doc(alias = "g_signal_group_connect_closure")]
|
||||
pub fn connect_closure(&self, signal_name: &str, after: bool, closure: RustClosure) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_group_connect_closure(
|
||||
self.to_glib_none().0,
|
||||
signal_name.to_glib_none().0,
|
||||
closure.as_ref().to_glib_none().0,
|
||||
after.into_glib(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_connect")]
|
||||
#[inline]
|
||||
pub fn connect<F>(&self, signal_name: &str, after: bool, callback: F)
|
||||
where
|
||||
F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
|
||||
{
|
||||
self.connect_closure(signal_name, after, RustClosure::new(callback));
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Like [`Self::connect`] but doesn't require a `Send+Sync` closure. Signal emission will
|
||||
/// panic if the signal on the current target is emitted from a different thread from the
|
||||
/// thread that connected the signal.
|
||||
#[inline]
|
||||
pub fn connect_local<F>(&self, signal_name: &str, after: bool, callback: F)
|
||||
where
|
||||
F: Fn(&[Value]) -> Option<Value> + 'static,
|
||||
{
|
||||
self.connect_closure(signal_name, after, RustClosure::new_local(callback));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn connect_notify<F>(&self, name: Option<&str>, callback: F)
|
||||
where
|
||||
F: Fn(&crate::Object, &crate::ParamSpec) + Send + Sync + 'static,
|
||||
{
|
||||
let signal_name = if let Some(name) = name {
|
||||
format!("notify::{name}")
|
||||
} else {
|
||||
"notify".into()
|
||||
};
|
||||
|
||||
let closure = crate::RustClosure::new(move |values| {
|
||||
let obj = values[0].get().unwrap();
|
||||
let pspec = values[1].get().unwrap();
|
||||
callback(obj, pspec);
|
||||
|
||||
None
|
||||
});
|
||||
|
||||
self.connect_closure(&signal_name, false, closure);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn connect_notify_local<F>(&self, name: Option<&str>, callback: F)
|
||||
where
|
||||
F: Fn(&crate::Object, &crate::ParamSpec) + 'static,
|
||||
{
|
||||
let signal_name = if let Some(name) = name {
|
||||
format!("notify::{name}")
|
||||
} else {
|
||||
"notify".into()
|
||||
};
|
||||
|
||||
let closure = crate::RustClosure::new_local(move |values| {
|
||||
let obj = values[0].get().unwrap();
|
||||
let pspec = values[1].get().unwrap();
|
||||
callback(obj, pspec);
|
||||
|
||||
None
|
||||
});
|
||||
|
||||
self.connect_closure(&signal_name, false, closure);
|
||||
}
|
||||
|
||||
unsafe fn connect_bind_unsafe<F: Fn(&Self, &Object)>(&self, f: F) -> SignalHandlerId {
|
||||
unsafe extern "C" fn bind_trampoline<F: Fn(&SignalGroup, &Object)>(
|
||||
this: *mut crate::gobject_ffi::GSignalGroup,
|
||||
instance: *mut crate::gobject_ffi::GObject,
|
||||
f: ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this), &from_glib_borrow(instance))
|
||||
}
|
||||
let f: Box<F> = Box::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"bind\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
bind_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box::into_raw(f),
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn connect_unbind_unsafe<F: Fn(&Self)>(&self, f: F) -> SignalHandlerId {
|
||||
unsafe extern "C" fn unbind_trampoline<F: Fn(&SignalGroup)>(
|
||||
this: *mut crate::gobject_ffi::GSignalGroup,
|
||||
f: ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this))
|
||||
}
|
||||
let f: Box<F> = Box::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"unbind\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
unbind_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box::into_raw(f),
|
||||
)
|
||||
}
|
||||
|
||||
#[doc(alias = "bind")]
|
||||
pub fn connect_bind<F: Fn(&Self, &Object) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe { self.connect_bind_unsafe(f) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Like [`Self::connect_bind`] but doesn't require a `Send+Sync` closure. Signal emission will
|
||||
/// panic if the signal is emitted from a different thread from the thread that connected the
|
||||
/// signal.
|
||||
pub fn connect_bind_local<F: Fn(&Self, &Object) + 'static>(&self, f: F) -> SignalHandlerId {
|
||||
let f = crate::thread_guard::ThreadGuard::new(f);
|
||||
|
||||
unsafe {
|
||||
self.connect_bind_unsafe(move |s, o| {
|
||||
(f.get_ref())(s, o);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "unbind")]
|
||||
pub fn connect_unbind<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId {
|
||||
unsafe { self.connect_unbind_unsafe(f) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Like [`Self::connect_unbind`] but doesn't require a `Send+Sync` closure. Signal emission
|
||||
/// will panic if the signal is emitted from a different thread from the thread that connected
|
||||
/// the signal.
|
||||
pub fn connect_unbind_local<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
|
||||
let f = crate::thread_guard::ThreadGuard::new(f);
|
||||
|
||||
unsafe {
|
||||
self.connect_unbind_unsafe(move |s| {
|
||||
(f.get_ref())(s);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use super::*;
|
||||
use crate as glib;
|
||||
use crate::{ObjectExt, StaticType};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use crate::subclass::{prelude::*, Signal};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SignalObject {}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SignalObject {
|
||||
const NAME: &'static str = "SignalObject";
|
||||
type Type = super::SignalObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for SignalObject {
|
||||
fn signals() -> &'static [Signal] {
|
||||
use once_cell::sync::Lazy;
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("sig-with-args")
|
||||
.param_types([u32::static_type(), String::static_type()])
|
||||
.build(),
|
||||
Signal::builder("sig-with-ret")
|
||||
.return_type::<String>()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wrapper! {
|
||||
pub struct SignalObject(ObjectSubclass<imp::SignalObject>);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_emit() {
|
||||
let group = SignalGroup::new::<SignalObject>();
|
||||
|
||||
let obj = Object::new::<SignalObject>();
|
||||
let store = Rc::new(RefCell::new(String::new()));
|
||||
group.connect_closure(
|
||||
"sig-with-args",
|
||||
false,
|
||||
glib::closure_local!(@watch obj, @strong store => move |o: &SignalObject, a: u32, b: &str| {
|
||||
assert_eq!(o, obj);
|
||||
store.replace(format!("a {a} b {b}"));
|
||||
})
|
||||
);
|
||||
group.connect_closure(
|
||||
"sig-with-ret",
|
||||
false,
|
||||
glib::closure_local!(@watch obj => move |o: &SignalObject| -> &'static crate::GStr {
|
||||
assert_eq!(o, obj);
|
||||
crate::gstr!("Hello")
|
||||
}),
|
||||
);
|
||||
group.set_target(Some(&obj));
|
||||
obj.emit_by_name::<()>("sig-with-args", &[&5u32, &"World"]);
|
||||
assert_eq!(*store.borrow(), "a 5 b World");
|
||||
let ret = obj.emit_by_name::<crate::GString>("sig-with-ret", &[]);
|
||||
assert_eq!(ret, "Hello");
|
||||
group.set_target(Object::NONE);
|
||||
let ret = obj.emit_by_name::<Option<String>>("sig-with-ret", &[]);
|
||||
assert_eq!(ret, None);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user