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

WIP: GTK::Simple::GladeApp #16

Closed
wants to merge 12 commits into from
Closed

Conversation

slobo
Copy link
Contributor

@slobo slobo commented Oct 11, 2015

This is an early code dump in order to gather comments. Idea is to use GtkBuilder to load Glade files and connect any widgets and signals in the said interface file to appropriate attributes and Supplies defined on the Perl class. E.g. the following does what you would expect:

class MyApp is GTK::Simple::GladeApp {
   has Supply $.button-one-press is gtk-signal('onButton1Pressed');
   has $.button2;
}
my $app = MyApp.new;
$app.button-one-signal.tap(-> ( $handler, $signal, $object) {
    say "got button1 signal $signal";
});
$app.button2.clicked.tap({ .label = 'Ouch' });

$app.run;

It's currently very simplistic and makes a lot of assumptions:

  • loads $*PROGRAM.glade
  • shows a window named "mainWindow"

TODO:

  • Figure out better signal handling (we used to invoke a method instead of providing supply)
  • Provide supply of signals
  • Ability to get to constructed objects by name (auto assign gtk_builder_get_object() based on attribute name) - hardcoded for ::Buttons
  • Ability to pass path to glade file?
  • Ability to specify name of main window, or auto-detect one from glade?
  • Refactor duplicate code shared with GTK::Simple::App, or maybe add glade loading to App and not have a separate class?

@jonathanstowe
Copy link
Contributor

Hi,
Sorry that no-one responded to this.

I've just started poking around with GTK::Simple in order to implement a couple of things that I needed for something I have in mind.

I think that there most definitely would be interest in this (despite the lack of discussion here,) unfortunately the PR has somewhat bit-rotted in the meantime: would it be possible for you to fix the merge conflicts, I'm quite happy to do so if you've already lost interest.

I think once it merges cleanly, it would be nice to bring it into a branch on this repository so that it makes it easier for other people to work on.

Anyway thanks a lot.

@jnthn
Copy link

jnthn commented May 19, 2016

I don't think generating methods to handle events is the right way to go. The key idea behind this module, when I first wrote it, was that events are delivered through supplies, which are vastly more composable.

@jonathanstowe
Copy link
Contributor

Yeah, I'm with you on that. A UI application is almost by definition reactive. I suspect that there is a little work to do to make it easier to implement all the signals with a supply to get the appropriate payload, but that will become apparent I'm sure.

But I'm fully in favour of the general idea of getting the GtkBuilder stuff in :)

@slobo
Copy link
Contributor Author

slobo commented May 19, 2016

Hey folks, thanks for taking interest. (I've been following jnthn's blog so I know he's been busy delivering goodies)

Can you provide a sample of what GtkBuilder usage would be like with supplies? I take it you object to both multi method handle-signal($signal-name) and method onSignalName() routes?

* loads `$*PROGRAM`.glade
* shows `mainWindow`
* connects signals to `handle-signal(Str)`
@slobo
Copy link
Contributor Author

slobo commented May 19, 2016

(btw, i've rebased on latest master, but didn't get a chance to test to see if it still works, hopefully tonight as modules.perl6 is down now so I can't get my machine working :( )

@jonathanstowe
Copy link
Contributor

Hi,
As I see it, at simplest, the callback subroutine passed to the gtk_builder_connect_signals_full rather than calling a method in the GladeApp object would emit to a Supplier with a Supply thereof exposed through the GladeApp object. I guess that what gets emitted would be another object that has sufficient information to identify and retrieve the source of the signal.

A more elaborate and possibly preferable scheme might involve each widget object in the application having it's own supply for each signal, which is basically what the existing signal-supply does right now except these would be connected automatically.

I'll have a play with this tomorrow to see what works best.

Based on finanalyst#16

The actual end-state would be more like each "handler" having
an individual supply, and that would be able to receive an
actual Perl object.
@jonathanstowe
Copy link
Contributor

Hi,
I've put a very very basic sketch of how it would work with supplies in the branch https://github.com/perl6/gtk-simple/tree/glade-app-supply

I think that a possible way it could be represented is by having an attribute trait (something like gtk-signal) which would be a supply that only gets the signals for a specific 'handler', so something like:

class  MyApp is GTK::Simple::GladeApp {
   has Supply $.button-one-press is gtk-signal('onButton1Pressed');
}

I'm just off out shopping now but will work this up when I get back :)

@jonathanstowe
Copy link
Contributor

Hi,
what is in that branch now is closer to how you could deal with "per handler" supplies, pretty much as described above.

Of course the elephant in the room here is that for all except the simplest button widgets you are going to want access to the object which the signal is for (to get its status or value or whatever,) as well as the other widgets in order to set their values.

I think the way to go here is to declare the objects in a similar way as for the signals, retrieve them by id (with gtk_builder_get_object) and construct the appropriate Perl objects with that, the signals can then be associated with the objects. For this there needs to be constructor candidate for the widgets that accepts the GObject (and really we can make them GWidget because all of the ones that we care about will be.)

Anyway any ideas welcome, as this is going to get quite complex quite qucikly :)

@slobo
Copy link
Contributor Author

slobo commented May 20, 2016

A worthwhile exercise might be to try and implement all other examples with glade and see where the pain comes from and get ideas where things could be simplified. Will try in a bit.

@slobo
Copy link
Contributor Author

slobo commented May 20, 2016

Right off the bat, in 01_helloworld.p6, we try to access both the button that is emitting the click, and the other button to toggle their Sensitive properties.
Since my p6-fu is not up to snuff, I just did a quick-n-dirty thing to support ::Button. the following works now:

class MyApp is GTK::Simple::GladeApp {
    has $.button;
    has $.second;
};
my $app = MyApp.new;

$app.button.clicked.tap({ .sensitive = False; $app.second.sensitive = True });
$app.second.clicked.tap({ $app.exit; });

$app.run;

@slobo
Copy link
Contributor Author

slobo commented May 20, 2016

(btw, I hope it's kosher to update original post as a summary of where we are at, let me know if you prefer different workflow)

@jonathanstowe
Copy link
Contributor

If you set a type constraint on the attributes then you can inspect that in your code and use that rather than hard coding ::Button. e.g:

class MyApp is GTK::Simple::GladeApp {
    has GTK::Simple::Button $.button;
    has GTK::Simple::Button $.second;
};

Then in the module :

$sig-attr.set_value(self, $sig-attr.type.new(gtk_widget => $gobject ));

Obviously there should be some check on whether it's defined.

Looking good 👍

@slobo
Copy link
Contributor Author

slobo commented May 20, 2016

Even worse than that, I can't figure out how to add method BUILD(:$!gtk_widget) to ::GtkWidget without touching every single subclass...

@jonathanstowe
Copy link
Contributor

Ah yes, that old not running the BUILD from a role gag :(

@slobo
Copy link
Contributor Author

slobo commented May 20, 2016

Is there a cure other than adding a multi to every class like I did with Button?

Also, was hoping to ask GObject about its type, to both require less typing, or also as an early check that .p6 and .glade agree about it, but having trouble finding anything in GTK docs...

@jonathanstowe
Copy link
Contributor

I've add #31 to cover off doing the first thing.

The second thing is difficult if not impossible directly from NativeCall as it requires some macros defined on GObject and parts of the introspection infrastructure which would only be available in compiled C programs, and it would be nice to avoid building a helper in C to do this (if only to show case the power of NativeCall on its own)

@slobo
Copy link
Contributor Author

slobo commented May 21, 2016

How about manually peeking into .glade file, would you find that acceptable? It should be fairly straightforward to extract the class for a given id, albeit may bring in a dependency on an XML parser unless we roll a regex ;)

@jonathanstowe
Copy link
Contributor

My assumption was that the classes would be generated from the xml anyway :)

@slobo
Copy link
Contributor Author

slobo commented May 21, 2016

Oh you mean automatic construction of class MyApp is Gtk::Simple::GladeApp with all signals and object accessors? Are you thinking to have a small utility to generate .pm?
Or you thinking it should all be done at runtime so this would be the usage

my $app = Gtk::Simple::GladeApp.new('hello.glade');
$app.button1.clicked.tap({...}); # Gtk::Simple::Button $.button1 from <object class='GtkButton' id='button1'>
$app.on-window-activate-focus.tap({...}); # Supply $.on-window-activate-focus from <window><signal name='activate-focus' handler='on-window-activate-focus'>

@jonathanstowe
Copy link
Contributor

No generate some module from the xml, that's what the other tools do

@slobo
Copy link
Contributor Author

slobo commented May 21, 2016

So, is the following how you see an example glade app:

MyApp.glade

<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="2.24"/>
  <!-- interface-naming-policy project-wide -->
  <object class="GtkWindow" id="window1">
    <signal name="activate-focus" handler="on_window1_activate_focus" swapped="no"/>
    <child>
      <object class="GtkButton" id="button1">
        <property name="label" translatable="yes">button 1</property>
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="receives_default">True</property>
      </object>
    </child>
  </object>
  <object class="GtkWindow" id="window2">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkButton" id="button2">
        <property name="label" translatable="yes">button 2
</property>
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="receives_default">True</property>
      </object>
    </child>
  </object>
</interface>
$ generate-pm.pl MyApp.glade 
writing MyApp/Window1.pm
writing MyApp/Window2.pm

MyApp/Window1.pm

class MyApp::Window1 is GTK::Simple::Window {
  has GTK::Simple::Button $.button1;
  has Supply $.on_window1_activate_focus is gtk-signal('on_window1_activate_focus');
}

Then manually do MyApp.pm

class MyApp is GTK::Simple::GladeApp {
  has MyApp::Window1 $window1;
  has MyApp::Window2 $window2;

  method run() {
    $window1.show;
    $window.$button1.clicked.tap({ $window2.show(); });
  }
}

How would updates to .glade happen - would we have ' #-- autogenerated section ' markers, or would one subclass the generated classes to extend them?

A benefit of doing it all at runtime is that you could use a simple script when you need to introspect generated classes for say documentation purposes, but you don't have to keep regenerating .pm6 every time you adjust glade with new objects and signals of interest...

method main($glade_filename) {
  say GTK::Simple::GladeApp.new($glade_filename).perl
}

@slobo
Copy link
Contributor Author

slobo commented May 21, 2016

On further thought, doing it at runtime would be hard to retain any IDE code completion, so scrap that bit.

@jnthn
Copy link

jnthn commented May 21, 2016

Note that one option you have in Perl 6 is to do the XML -> Class translation as part of a module's BEGIN time and poke them into the export table, so that precompilation will cache the produced types. This means lower runtime cost, but no code generation. :-)

@slobo
Copy link
Contributor Author

slobo commented May 21, 2016

Neat! Is there a mechanism for invalidating the precompilation cache, given that it would depend on external file?

@azawawi
Copy link
Contributor

azawawi commented Sep 4, 2016

Any update on this one?

@slobo
Copy link
Contributor Author

slobo commented Sep 11, 2016

I've brought it up to date with master. Now I'm blocked on #31 - the best way to add method BUILD(:$!gtk_widget) to every widget.

Is there a way to add it to ::Widget role, or is the only way to add multi method BUILD(:$!gtk_widget) to every widget? It's not a big job, just wondering if there is a more perl6ish way?

@JJ
Copy link
Contributor

JJ commented May 12, 2020

Well, there's a bit of everything in here: conflicts, requests for change... Do we want this? Do we need this?

@Xliff
Copy link

Xliff commented May 12, 2020

Depends on comment from the contributors. Though it looks like there hasn't been any work on it for almost 3 years.

It looks useful, though. I say this with utter bias.

@JJ
Copy link
Contributor

JJ commented May 12, 2020

Could you maybe try and rescue it?

@Xliff
Copy link

Xliff commented May 12, 2020

Hmmm...

Yeah, I could give it a try, @JJ

@MARTIMM
Copy link

MARTIMM commented May 12, 2020 via email

@finanalyst
Copy link
Owner

I'm removing this PR because it's old compared to other changes, so I wouldn't know how to untangle all the conflicts.
If adding Glade is still compatible with GTK-Simple, or the changes are straight forward, open an new PR

@finanalyst finanalyst closed this Feb 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants