Skip to content

Instantly share code, notes, and snippets.

@bwindels
Created April 27, 2021 15:47
Show Gist options
  • Select an option

  • Save bwindels/36c0e3b17c5556972d84cbe06caa41b2 to your computer and use it in GitHub Desktop.

Select an option

Save bwindels/36c0e3b17c5556972d84cbe06caa41b2 to your computer and use it in GitHub Desktop.
#!/usr/bin/gjs
/*
https://developer.gnome.org/platform-overview/unstable/tour-gjs.html.en
https://gitlab.gnome.org/GNOME/gjs/wikis/Home
examples at https://github.com/optimisme/gjs-examples
docs at https://devdocs.baznga.org/gtk30~3.22.12/
*/
imports.gi.versions.Gtk = '3.0';
const Gtk = imports.gi.Gtk;
const Mainloop = imports.mainloop;
const GObject = imports.gi.GObject;
class HelloWorld {
constructor() {
this.app = new Gtk.Application();
this.app.connect('activate', this._onActivate.bind(this));
this.app.connect('startup', this._onStartup.bind(this));
}
_onActivate() {
this._window.show_all();
}
async _onStartup() {
try {
const builder = new Gtk.Builder();
builder.add_from_file('ui.glade');
this._window = builder.get_object('mainwindow');
this.app.add_window(this._window);
builder.get_object('header').set_title('haha!');
const list = builder.get_object('list');
list.set_model(this._createModel());
const column = new Gtk.TreeViewColumn({title: "Title"});
const cell = new Gtk.CellRendererText();
column.pack_start(cell, true);
column.add_attribute(cell, "text", 0);
list.append_column(column);
await new Promise(resolve => Mainloop.timeout_add(2000, resolve));
builder.get_object('header').set_subtitle('t macheert gewoon!');
} catch (err) {
log(err);
}
}
_createModel() {
const store = new Gtk.ListStore();
store.set_column_types([GObject.TYPE_STRING]);
store.set_value(store.append(), 0, "hello");
store.set_value(store.append(), 0, "world");
store.set_value(store.append(), 0, "foo bar");
return store;
}
}
new HelloWorld().app.run(ARGV);
/**
* looks like for a nice updatable list we will need to use ListBox (as fractal does).
* It can take a Gio.ListModel, which can only emit an event for adding and removing items,
* not for changing items. For this, it relies on the items being GObject's,
* which have an notify (https://gjs-docs.gnome.org/gobject20~2.66p/gobject.object#signal-notify) signal for when it changes.
*
* So to convert a ObservableList to a ListModel,
* we'd have to wrap all the items in a GObject wrapper,
* declaring each property so we can bind to it?
*/
/**
* In the GObject wrapper,
* we might have to cache the values of all (or at least the ones used on an object)
* the properties (like we do for the html binding) to know if the property has actually changed.
*
* E.g. in the ObservableListModel we would receive an onUpdate(index, value, params) and we would get the wrapper
* for that index, and call checkChanged() or something, where it goes over all its properties and compares the
* ones in the cache vs the ones returned by the original view model, and calls set_property (which emits the notify signal) for each changed?
*
* Alternatively, we start taking the arguments to emitChange([]) seriously... should we? hmmm
*
* We could then have the GObject be a stateless passthrough for our model... we declare the props with functions/closures? that just get it from the view model.
* https://ptomato.wordpress.com/2017/07/14/inventing-gobject-es6-classes/
* http://videos.guadec.org/2017/turing20-philip_chimento-modern_javascript.webm
*
* Hmmm, or perhaps we can manually update the bindings for a collection and not use the gtk bindings.
*
* E.g. the widget returned from GtkListBoxCreateWidgetFunc can have an updateBindings() method that we call somehow
* when an onUpdate is received, by getting the widget at the index (gtk_list_box_get_row_at_index), and calling updateBindings.
*
* Hmm, but that doesn't change the fact that Gio.ListModel needs to return a GObject ...
* so we might as well go with the wrapper if we need a GObject anyway.
* Probably best to go with the standard binding initially anyway
*/
// const RoomTileWrapper = createWrapper({
// hidden: GObject.TYPE_BOOL,
// url: GObject.TYPE_STRING,
// isOpen: GObject.TYPE_BOOL,
// isUnread: GObject.TYPE_BOOL,
// name: GObject.TYPE_STRING,
// avatarUrl: GObject.TYPE_STRING,
// });
// all properties would be readonly, as we only support one-way binding.
// how can we call methods on the viewmodel?
// wrapper.original.method() ?
// const listModel = new ObservableListModel(list, RoomTileWrapper);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment