Skip to content

Allow implementing a GAction interface #1640

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@ path = "object_subclass/main.rs"
[[bin]]
name = "virtual_methods"
path = "virtual_methods/main.rs"

[[bin]]
name = "gio_action_impl"
path = "gio_action_impl/main.rs"
89 changes: 89 additions & 0 deletions examples/gio_action_impl/action.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use gio::{prelude::*, subclass::prelude::*};

mod imp {
use super::*;
use std::cell::OnceCell;

#[derive(glib::Properties, Default)]
#[properties(wrapper_type = super::RenamedAction)]
pub struct RenamedAction {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The properties macro allows overriding properties by the way

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it a try but received an error

(process:718176): GLib-GObject-CRITICAL **: 22:59:07.010: When installing property: type 'ExampleRenamedAction' already has a property named 'name'

I guess it's because of the sequence of initialization. When I define a class and implement an interface then class installs properties first and interface fails to install them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way how you do it now should work or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not work. I guess It works as expected in the cases like

class A implements IFace

class B extends A implements IFace {
   override property x of IFace
}

where initialization sequence is: A, IFace, B. So, when B is initializing IFace is already there and could be overriden.

but does not work for

class B implements IFace {
   override property x of IFace
}

where initialization sequence is: B, IFace. So, B has nothing to override yet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You didn't find a solution to this yet, right? This probably should be solved independent of this PR (please create an issue with all you remember)

#[property(get, construct_only)]
pub new_name: OnceCell<glib::GString>,

#[property(get, construct_only)]
pub action: OnceCell<gio::Action>,
}

#[glib::object_subclass]
impl ObjectSubclass for RenamedAction {
const NAME: &'static str = "ExampleRenamedAction";
type Type = super::RenamedAction;
type Interfaces = (gio::Action,);
}

#[glib::derived_properties]
impl ObjectImpl for RenamedAction {
fn properties() -> &'static [glib::ParamSpec] {
Self::derived_properties()
}

fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
if !self.delegate_set_property(id, value, pspec) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If multiple interfaces like this are implemented, the method name would probably conflict. Should we write the interface name in the method name (delegate_action_set_property()), or change the example to specify the trait (<Self as ActionImplExt>::delegate_set_property(self, id, value, pspec))?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far there are only Actions and Editable.i rather keep it this way until we find more cases like this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd already have this problem then if you implement both interfaces in the same type :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is 0 use case for that. You would rather implement GActionMap instead

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but if we want to solve this problem later we need to change API in even more places so it seems useful to figure this out from the beginning. I'm not going to block this PR on this though, just don't tell me in a few years that I didn't warn you :P

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal is to basically make the derived_properties macro automatically generate those bits for you. I will send a patch for it once this PR lands

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

self.derived_set_property(id, value, pspec);
}
}

fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.delegate_get_property(id, pspec)
.unwrap_or_else(|| self.derived_property(id, pspec))
}
}

impl ActionImpl for RenamedAction {
fn name(&self) -> glib::GString {
self.obj().new_name()
}

fn parameter_type(&self) -> Option<glib::VariantType> {
self.obj().action().parameter_type()
}

fn state_type(&self) -> Option<glib::VariantType> {
self.obj().action().state_type()
}

fn state_hint(&self) -> Option<glib::Variant> {
self.obj().action().state_hint()
}

fn is_enabled(&self) -> bool {
self.obj().action().is_enabled()
}

fn state(&self) -> Option<glib::Variant> {
self.obj().action().state()
}

fn change_state(&self, value: glib::Variant) {
self.obj().action().change_state(&value);
}

fn activate(&self, parameter: Option<glib::Variant>) {
self.obj().action().activate(parameter.as_ref());
}
}
}

glib::wrapper! {
pub struct RenamedAction(ObjectSubclass<imp::RenamedAction>)
@implements gio::Action;
}

impl RenamedAction {
pub fn new(name: &str, action: &impl IsA<gio::Action>) -> Self {
glib::Object::builder()
.property("new-name", name)
.property("action", action)
.build()
}
}
22 changes: 22 additions & 0 deletions examples/gio_action_impl/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
mod action;

use gio::prelude::*;

fn main() {
let action = gio::SimpleAction::new("bark", Some(glib::VariantTy::STRING));
action.connect_activate(|_, p| {
let target = p.unwrap().str().unwrap();
println!("Woof, {}!", target);
});

let renamed_action = action::RenamedAction::new("meow", &action);

let group = gio::SimpleActionGroup::new();
group.add_action(&action);
group.add_action(&renamed_action);

println!("actions = {:?}", group.list_actions());

group.activate_action("bark", Some(&"postman".to_variant()));
group.activate_action("meow", Some(&"milkman".to_variant()));
}
Loading
Loading