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

Define Gtk closure expressions in code when the UI file is NOT a template. #667

Open
rolandlo opened this issue Oct 3, 2023 · 9 comments · Fixed by #690
Open

Define Gtk closure expressions in code when the UI file is NOT a template. #667

rolandlo opened this issue Oct 3, 2023 · 9 comments · Fixed by #690

Comments

@rolandlo
Copy link
Contributor

rolandlo commented Oct 3, 2023

Suppose we have a .blp-file that defines a template. Then we can use Gtk closure expression defined in the (say javascript) code like this for example:

UI file main.blp

using Gtk 4.0;
using Adw 1;

template $CustomWidget : Adw.PreferencesGroup {
  Adw.ComboRow cb {
    title: _("Make a selection:");
    
    model:
    StringList {
      strings ["One", "Two", "Three", "Four"]
    };
  }
  
  Adw.SwitchRow sw1 {  // should only be visible when option "One", "Two" or "Three" is selected.
    title: _("Switch one");
    active: true;
    visible: bind $onlyOneTwoThree(cb.selected-item) as <bool>;
  }
  
  Adw.SwitchRow sw2 { // should only be visible when option "Three" or "Four" is selected
    title: _("Switch two");
    visible: bind $onlyThreeFour(cb.selected-item) as <bool>;
  }
  
  Adw.ActionRow {
    title: _("Status:");
    Label { // should display some status message depending on sw1.visible, sw2.visible and cb.selected-item
      label: bind $msg(sw1.visible, sw2.visible, cb.selected-item) as <string>;
    }
  }
 }

Code file main.js

import GObject from "gi://GObject";
import Gtk from "gi://Gtk";
import Adw from "gi://Adw";

const CustomWidget = GObject.registerClass(
  {
    GTypeName: "CustomWidget",
    Template: workbench.template,
  },
  class CustomWidget extends Adw.PreferencesGroup {
    onlyOneTwoThree(_self, item) {
      const str = item.string;
      return str === "One" || str === "Two" || str === "Three";
    }
    onlyThreeFour(_self, item) {
      const str = item.string;
      return str === "Three" || str === "Four";
    }
    msg(_self, active1, active2, item) {
      const str = item.string;
      if (active1 && active2) {
        return `"${str}" was selected, 2 visible`;
      } else if (active1 || active2) {
        return `"${str}" was selected, 1 visible`;
      } else {
        return "0 active. Discard selection";
      }
    }
  },
);

const box = new Gtk.Box({ orientation: "vertical" });
box.append(new CustomWidget());
workbench.preview(box);

Now suppose I want to have the same behavior in case that the UI file does NOT define a template (remove

template $CustomWidget : 

from the fourth line of main.blp)

How can we define the Gtk closure expressions onlyOneTwoThree, onlyThreeFour and msg in the code? There doesn't seem to be an easy way to do so. According to @andyholmes (conversation in the matrix channel) these closures should be added to Workbench's builder scope in some way (compare gjs guide), yet apparently Workbench is not set up to use closures at the moment.

So I'd like to request Workbench to support closures in a way that it is easy to use and documented (maybe in a Library demo).

@sonnyp
Copy link
Contributor

sonnyp commented Oct 14, 2023

Thanks for filing a clear issue. This is something I've wanted as well.

Related, during a Workshop someone was confused about the lack of signal handlers. https://floss.social/@sonny/110816126000858204

The main issues are:

  • We need a simple API for it and it doesn't exist in GTK currently
  • I would like this API to be exactly or as close as possible to what you would use once moving away from Workbench

I've been proposing the following for GJS https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/853

Let's move forward, here is a plan:

  1. Add the API in Workbench but don't use it in Library and make it log a warning "Experimental stuff"
  2. Make the API available in Rust and Vala as well
  3. See where we go from there and how we can "upstream" this API
  4. Expose the API and use it in Library

@sonnyp
Copy link
Contributor

sonnyp commented Oct 21, 2023

@rolandlo are you able to test / use Workbench devel (branch main)?

If you're gonna use it, please do share feedback and thoughts. Thanks!

The experimental workbench.build API can be use like so:

Blueprint

using Gtk 4.0;
using Adw 1;

Adw.PreferencesGroup preferences_group {
  Adw.ComboRow cb {
    title: _("Make a selection:");
    
    model:
    StringList {
      strings ["One", "Two", "Three", "Four"]
    };
  }
  
  Adw.SwitchRow sw1 {  // should only be visible when option "One", "Two" or "Three" is selected.
    title: _("Switch one");
    active: true;
    visible: bind $onlyOneTwoThree(cb.selected-item) as <bool>;
  }
  
  Adw.SwitchRow sw2 { // should only be visible when option "Three" or "Four" is selected
    title: _("Switch two");
    visible: bind $onlyThreeFour(cb.selected-item) as <bool>;
  }
  
  Adw.ActionRow {
    title: _("Status:");
    Label { // should display some status message depending on sw1.visible, sw2.visible and cb.selected-item
      label: bind $msg(sw1.visible, sw2.visible, cb.selected-item) as <string>;
    }
  }
  
  Button {
    label: "Signal handler";
    clicked => $onButtonClicked();
  }
}

JavaScript

import GObject from "gi://GObject";
import Gtk from "gi://Gtk";
import Adw from "gi://Adw";

const { preferences_group } = workbench.build({
  onlyOneTwoThree(_self, item) {
    const str = item.string;
    return str === "One" || str === "Two" || str === "Three";
  },
  onlyThreeFour(_self, item) {
    const str = item.string;
    return str === "Three" || str === "Four";
  },
  msg(_self, active1, active2, item) {
    log("msg");
    const str = item.string;
    if (active1 && active2) {
      return `"${str}" was selected, 2 visible`;
    } else if (active1 || active2) {
      return `"${str}" was selected, 1 visible`;
    } else {
      return "0 active. Discard selection";
    }
  },
  onButtonClicked(...args) {
    console.log(...args);
  },
});

workbench.preview(preferences_group);

@rolandlo
Copy link
Contributor Author

@rolandlo are you able to test / use Workbench devel (branch main)?

Yes, I can test the main branch. It works great in the example. The syntax is nice. However, how would the JS code look for a Blueprint file like this:

using Gtk 4.0;
using Adw 1;

Adw.PreferencesWindow preferences_window {
  Adw.PreferencesGroup preferences_group {  
    Button {
      label: "Signal handler";
      clicked => $onButtonClicked();
    }
  }
}

With the obvious looking

import GObject from "gi://GObject";
import Gtk from "gi://Gtk";
import Adw from "gi://Adw";

const { preferences_window } = workbench.build({
  onButtonClicked,
});

function onButtonClicked(...args) {
  console.log(...args);
}

workbench.preview(preferences_window);

I get a message
grafik
when clicking on the button.

@sonnyp
Copy link
Contributor

sonnyp commented Oct 23, 2023

You need to press the "Run" button first. Related #643

@rolandlo
Copy link
Contributor Author

I did, but that doesn't help. Note that when I change Adw.PreferencesWindow to Box it also works.

@sonnyp
Copy link
Contributor

sonnyp commented Oct 23, 2023

I'm not sure why that is but preferences_window.present() instead of workbench.preview(preferences_window) seems to work.

@rolandlo
Copy link
Contributor Author

Right, it does. I will do further tests then.

@rolandlo
Copy link
Contributor Author

Everything works fine now with what I wanted to achieve with this feature request. See the redesigned Xournal++ preferences window code/UI in case you'd like to check it out.

@sonnyp
Copy link
Contributor

sonnyp commented Nov 5, 2023

@rolandlo that's great to hear.

I'm glad Workbench is useful to Xournal++

I'll leave this open as there are a couple of things left to do here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants