Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE REQUEST] Newtype support for #[property] #983

Closed
YaLTeR opened this issue Feb 11, 2023 · 3 comments · Fixed by #1000 · May be fixed by #984
Closed

[FEATURE REQUEST] Newtype support for #[property] #983

YaLTeR opened this issue Feb 11, 2023 · 3 comments · Fixed by #1000 · May be fixed by #984
Labels
enhancement New feature or request

Comments

@YaLTeR
Copy link
Contributor

YaLTeR commented Feb 11, 2023

I have a newtype over f64:

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ScaleRequest {
    FitToAllocation,
    Set(f64),
}

It maps one-to-one to f64 (0. maps to the first item, others map to the second item). I'd like to use it with the properties macro. For GObject it should appear as an f64 property.

Currently this seems to require implementing HasParamSpec, ToValue, From<ScaleRequest> for Value (for some reason?) and FromValue, the last of which requires unsafe code:

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ScaleRequest {
    FitToAllocation,
    Set(f64),
}

impl From<f64> for ScaleRequest {
    fn from(value: f64) -> Self {
        if value == 0. {
            ScaleRequest::FitToAllocation
        } else {
            ScaleRequest::Set(value.clamp(0., 10.))
        }
    }
}

impl glib::HasParamSpec for ScaleRequest {
    type ParamSpec = glib::ParamSpecDouble;
    type SetValue = Self;
    type BuilderFn = fn(&str) -> glib::ParamSpecDoubleBuilder;

    fn param_spec_builder() -> Self::BuilderFn {
        Self::ParamSpec::builder
    }
}

impl From<ScaleRequest> for glib::Value {
    fn from(value: ScaleRequest) -> Self {
        value.to_value()
    }
}

impl glib::ToValue for ScaleRequest {
    fn to_value(&self) -> glib::Value {
        match *self {
            ScaleRequest::FitToAllocation => 0.,
            ScaleRequest::Set(scale) => scale,
        }
        .to_value()
    }

    fn value_type(&self) -> glib::Type {
        f64::static_type()
    }
}

unsafe impl<'a> glib::value::FromValue<'a> for ScaleRequest {
    type Checker = glib::value::GenericValueTypeChecker<f64>;

    unsafe fn from_value(value: &'a glib::Value) -> Self {
        f64::from_value(value).into()
    }
}

Would be great if this could be representable without unsafe code with some kind of pair of From<f64> for T + From<T> for f64.

It's also worth noting that I could instead implement Property with Value = f64 and custom get and set returning f64. However, that would result in my Rust type having getter and setter working with f64 instead of the real type, which is not as nice.

@YaLTeR YaLTeR added the enhancement New feature or request label Feb 11, 2023
@YaLTeR
Copy link
Contributor Author

YaLTeR commented Feb 11, 2023

cc @sdroege @ranfdev

@sdroege
Copy link
Member

sdroege commented Feb 11, 2023

I think what would be nice to have is something like a transform = from attribute on the macro, which would be used together with type. Then you could in your case do type = f64, transform = from and provide From impls in both directions for your enum.

We might want to add other ways of transformation later, not sure. If you can't think of anything else then maybe just a from attribute would be sufficient.

@ranfdev
Copy link
Member

ranfdev commented Feb 11, 2023

Instead of requiring transform = from, the PR I just pushed always calls From::from. That enables supporting other types which don't implement FromValue, as ThreadGuard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
3 participants