Closed romgrk closed 4 years ago
Sent from cm9tZ3JrLmNjQGdtYWlsLmNvbQ== (base64)
I have a question: is every method returning an Array or I need to gir-scan all methods of each prototype / class to figure out the return type and monkey-patch that ?
Every method that has more than 1 return value returns an array.
The easiest way to find which ones do so would be to use ./lib/inspect.js
, as follow:
const inspect = require('./lib/inspect')
const gtk = inspect.parseNamespace('Gtk')
fnInfos = inspect.infos.filter(i => i.infoType === 'function' && i.return_tag !== 'void' && i.args.some(a => a.direction != 'IN'))
fnInfos.forEach(f => console.log(inspect.formatFunction(f)))
I guess my question was rather: if a method returns an Array, as actual return value, what should happen?
Well this is the reason we need overrides: there's not always a rule as to what should happen. The goal here would be to make the API look nicer for "popular" methods of "popular" namespaces. (Hence why I just mention Gtk/Gdk in the title).
Some general cases/examples:
1) Some functions have return values that can be ignored, usually because they're not relevant in a javascript context. One such example is described here: https://gitlab.gnome.org/GNOME/gjs/issues/66 In those cases, we're left with a single relevant return value, which should be the JS return value.
2) E.g. gtk_widget_get_request_size
: methods which return more than one relevant return value.
In those cases, we need to return an object with properties named accordingly.
3) Functions as the following one:
gboolean
g_file_get_contents (const gchar *filename,
gchar **contents,
gsize *length,
GError **error);
Those return a boolean to allow the following style in C:
GError error;
if (!g_file_get_contents(..., &error)) {
// Deal with error
}
This is irrelevant in JS, because an error would be thrown and the boolean return value is irrelevant. In those cases, we should just strip the boolean return value, and deal with the rest of the arguments.
(Note that this example is in GLib, I'm not sure if or how many of these there are in Gtk/Gdk.)
(Note also that in this example, only the contents
value is relevant in javascript, so it would fold back into category 1)
That's what I could think about, there might be other things to deal with that we'll discover as we progress.
it's not fully clear how am I supposed to patch these functions
/*
* Gtk-3.0.js
*/
const inspect = require('../inspect');
const multiReturn = i =>
i.infoType === 'function' &&
i.return_tag !== 'void' &&
i.args.some(a => a.direction != 'IN');
const parse = (obj, name, whole) => {
const nmsp = inspect.parseNamespace(whole);
const fnInfos = inspect.infos.splice(0).filter(multiReturn);
fnInfos.forEach(function (f) {
console.log(f.name);
if (f.parent === name) {
/*
// nope, it doesn't work
const method = obj[f.name];
obj[f.name] = function () {
const result = method.call(this); // apply, arguments, whatever
return result;
};
*/
} else {
// parse(f.parent, f.parent.name, whole + '.' + f.parent.name);
}
});
};
exports.apply = (Gtk) => {
parse(Gtk, 'Gtk', 'Gtk');
};
The inspect.parseNamespace
also return an object, instead of an array, but even mapping its keys won't work through that filter neither.
Sorry I am a bit rusty with Gtk stuff in node, I used to do things slightly differently in GJS https://github.com/cgjs/cgjs/blob/master/packages/cgjs/cgjs/camel.js#L68
Sorry, I might not have explained correctly. The snippet of code I included above is only useful to retrieve a list of functions that might need patching. But I was thinking, it would be better to manually patch the functions that need patching. Overrides are there to fix edge-cases for which we can't possibly include a logic that would handle all cases.
Any other JS specific thing can also be added there. For example, PyGObject makes Gtk.TreePath
iterable. This could also be done in JS by implementing Gtk.TreePath[Symbol.iterator]
.
So we would start with something like this:
/*
* Gtk-3.0.js
*/
exports.apply = (Gtk) => {
/***
* Gtk.Widget
***/
/**
* @typedef {Object} Dimension
* @property {number} width
* @property {number} height
*/
const originalGetSizeRequest = Gtk.Widget.prototype.getSizeRequest
/**
* Gtk.Widget.prototype.getSizeRequest
* @returns {Dimension}
*/
Gtk.Widget.prototype.getSizeRequest = function() {
const [width, height] = originalGetSizeRequest.call(this)
return { width, height }
}
/***
* Gtk.TreePath
***/
Gtk.TreePath.prototype[Symbol.iterator] = function () {
// I've no idea how this works but check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
}
// Then, any other function/thing that needs patching
}
Note that I'm including jsdocs because it might help for #24, we could parse those so that TypeScript annotations include overrides.
that filter returns 194 methods to patch though ... this is going to be ready for Christmas if I have to also understand what each method returns and document it via jsdocs ... just saying.
Side note: did this project also become a no-semicolon one ? (just to clarify how to contribute)
Yea, I know. The idea is not to patch everything, just the things that it makes sense to patch. Maybe there's not that much stuff to patch, I just haven't looked yet Don't worry about JSDocs for now, we're not there yet anyway.
Side note: did this project also become a no-semicolon one ? (just to clarify how to contribute)
Well, this project hasn't chosen any style guide yet, so the code that has been added reflects the personal preferences of the person who wrote it. I don't use semi-colons in javascript because they're not necessary and less characters means less input for your brain to parse. That said, if you have strong opinions on semi-colons I'm ok with that. Maybe we could use prettier? Do you have any style guide proposition?
(I've added you to the project's collaborators. I usually use a branch workflow for major changes that possibly require discussions, and push to master for minor changes like these)
Maybe we could use prettier?
I don't care, but I prefer semi colons (and I have strong opinions about it but that's my preference).
I usually use a branch workflow for major changes that possibly require discussions, and push to master for minor changes like these
works for me.
I'm not sure Gtk brings Widget or any class at all with it ...
exports.apply = (Gtk) => {
console.log(Gtk.Widget); // undefined
};
does that work for you?
Anyway, this is the list I could extract on Mac
AccelGroup.query (accel_key: guint32, accel_mods: flags.ModifierType, n_entries: guint32OUTE): struct.AccelGroupEntry[]
AccelMap.lookup_entry (accel_path: utf8, key: struct.AccelKeyOUT): gboolean
ActionGroup.query_action (action_name: utf8, enabled: gbooleanOUTE, parameter_type: struct.VariantTypeOUTE, state_type: struct.VariantTypeOUTE, state_hint: struct.VariantOUTE, state: struct.VariantOUTE): gboolean
Buildable.custom_tag_start (builder: object.Builder, child: object.Object, tagname: utf8, parser: struct.MarkupParserOUT, data: voidOUTE): gboolean
Builder.value_from_string (pspec: object.ParamSpec, string: utf8, value: struct.ValueOUT): gboolean
Builder.value_from_string_type (type: GType, string: utf8, value: struct.ValueOUT): gboolean
CellAreaClass.list_cell_properties (n_properties: guint32OUTE): object.ParamSpec[]
CellArea.get_cell_at_position (context: object.CellAreaContext, widget: object.Widget, cell_area: struct.Rectangle, x: gint32, y: gint32, alloc_area: struct.RectangleOUT): object.CellRenderer
CellView.get_size_of_row (path: struct.TreePath, requisition: struct.RequisitionOUT): gboolean
Clipboard.wait_for_rich_text (buffer: object.TextBuffer, format: struct.AtomOUT, length: guint64OUTE): guint8
Clipboard.wait_for_targets (targets: struct.Atom[]OUTE, n_targets: gint32OUTE): gboolean
ColorSelection.palette_from_string (str: utf8, colors: struct.Color[]OUTE, n_colors: gint32OUTE): gboolean
ComboBox.get_active_iter (iter: struct.TreeIterOUT): gboolean
ContainerClass.list_child_properties (n_properties: guint32OUTE): object.ParamSpec[]
Container.get_focus_chain (focusable_widgets: List<object.Widget>OUTC): gboolean
Editable.get_selection_bounds (start_pos: gint32OUTE, end_pos: gint32OUTE): gboolean
GestureDrag.get_offset (x: gdoubleOUTE, y: gdoubleOUTE): gboolean
GestureDrag.get_start_point (x: gdoubleOUTE, y: gdoubleOUTE): gboolean
GestureMultiPress.get_area (rect: struct.RectangleOUT): gboolean
GestureSwipe.get_velocity (velocity_x: gdoubleOUTE, velocity_y: gdoubleOUTE): gboolean
Gesture.get_bounding_box (rect: struct.RectangleOUT): gboolean
Gesture.get_bounding_box_center (x: gdoubleOUTE, y: gdoubleOUTE): gboolean
Gesture.get_point (sequence: struct.EventSequence, x: gdoubleOUTE, y: gdoubleOUTE): gboolean
Gradient.resolve (props: object.StyleProperties, resolved_gradient: struct.PatternOUTE): gboolean
IMContext.get_surrounding (text: utf8OUTE, cursor_index: gint32OUTE): gboolean
IconInfo.get_attach_points (points: struct.Point[]OUTE, n_points: gint32OUTE): gboolean
IconInfo.get_embedded_rect (rectangle: struct.RectangleOUT): gboolean
IconInfo.load_symbolic (fg: struct.RGBA, success_color: struct.RGBA, warning_color: struct.RGBA, error_color: struct.RGBA, was_symbolic: gbooleanOUTE): object.Pixbuf
IconInfo.load_symbolic_finish (res: interface.AsyncResult, was_symbolic: gbooleanOUTE): object.Pixbuf
IconInfo.load_symbolic_for_context (context: object.StyleContext, was_symbolic: gbooleanOUTE): object.Pixbuf
IconInfo.load_symbolic_for_context_finish (res: interface.AsyncResult, was_symbolic: gbooleanOUTE): object.Pixbuf
IconInfo.load_symbolic_for_style (style: object.Style, state: enum.StateType, was_symbolic: gbooleanOUTE): object.Pixbuf
IconSize.lookup (size: gint32, width: gint32OUTE, height: gint32OUTE): gboolean
IconSize.lookup_for_settings (settings: object.Settings, size: gint32, width: gint32OUTE, height: gint32OUTE): gboolean
IconView.get_cell_rect (path: struct.TreePath, cell: object.CellRenderer, rect: struct.RectangleOUT): gboolean
IconView.get_cursor (path: struct.TreePathOUTE, cell: object.CellRendererOUT): gboolean
IconView.get_dest_item_at_pos (drag_x: gint32, drag_y: gint32, path: struct.TreePathOUTE, pos: enum.IconViewDropPositionOUTE): gboolean
IconView.get_item_at_pos (x: gint32, y: gint32, path: struct.TreePathOUTE, cell: object.CellRendererOUT): gboolean
IconView.get_tooltip_context (x: gint32INOUTE, y: gint32INOUTE, keyboard_tip: gboolean, model: interface.TreeModelOUT, path: struct.TreePathOUTE, iter: struct.TreeIterOUT): gboolean
IconView.get_visible_range (start_path: struct.TreePathOUTE, end_path: struct.TreePathOUTE): gboolean
Label.get_selection_bounds (start: gint32OUTE, end: gint32OUTE): gboolean
LevelBar.get_offset_value (name: utf8, value: gdoubleOUTE): gboolean
Popover.get_pointing_to (rect: struct.RectangleOUT): gboolean
PrintContext.get_hard_margins (top: gdoubleOUTE, bottom: gdoubleOUTE, left: gdoubleOUTE, right: gdoubleOUTE): gboolean
PrintSettings.get_page_ranges (num_ranges: gint32OUTE): struct.PageRange[]
RecentChooser.get_uris (length: guint64OUTE): utf8[]
RecentInfo.get_application_info (app_name: utf8, app_exec: utf8OUT, count: guint32OUTE, time_: gint64OUTE): gboolean
RecentInfo.get_applications (length: guint64OUTE): utf8[]
RecentInfo.get_groups (length: guint64OUTE): utf8[]
Scrollable.get_border (border: struct.BorderOUT): gboolean
SelectionData.get_data (length: gint32OUTE): guint8[]
SelectionData.get_targets (targets: struct.Atom[]OUTC, n_atoms: gint32OUTE): gboolean
StatusIcon.get_geometry (screen: object.ScreenOUT, area: struct.RectangleOUT, orientation: enum.OrientationOUTE): gboolean
StyleContext.has_region (region_name: utf8, flags_return: flags.RegionFlagsOUTE): gboolean
StyleContext.lookup_color (color_name: utf8, color: struct.RGBAOUT): gboolean
StyleContext.state_is_running (state: enum.StateType, progress: gdoubleOUTE): gboolean
StyleProperties.get_property (property: utf8, state: flags.StateFlags, value: struct.ValueOUTE): gboolean
StyleProvider.get_style_property (path: struct.WidgetPath, state: flags.StateFlags, pspec: object.ParamSpec, value: struct.ValueOUT): gboolean
Style.lookup_color (color_name: utf8, color: struct.ColorOUT): gboolean
SymbolicColor.resolve (props: object.StyleProperties, resolved_color: struct.RGBAOUT): gboolean
TargetList.find (target: struct.Atom, info: guint32OUTE): gboolean
TextBuffer.get_deserialize_formats (n_formats: gint32OUTE): struct.Atom[]
TextBuffer.get_selection_bounds (start: struct.TextIterOUT, end: struct.TextIterOUT): gboolean
TextBuffer.get_serialize_formats (n_formats: gint32OUTE): struct.Atom[]
TextBuffer.serialize (content_buffer: object.TextBuffer, format: struct.Atom, start: struct.TextIter, end: struct.TextIter, length: guint64OUTE): guint8[]
TextIter.backward_search (str: utf8, flags: flags.TextSearchFlags, match_start: struct.TextIterOUT, match_end: struct.TextIterOUT, limit: struct.TextIter): gboolean
TextIter.forward_search (str: utf8, flags: flags.TextSearchFlags, match_start: struct.TextIterOUT, match_end: struct.TextIterOUT, limit: struct.TextIter): gboolean
TextIter.get_attributes (values: struct.TextAttributesOUT): gboolean
TextView.get_iter_at_location (iter: struct.TextIterOUT, x: gint32, y: gint32): gboolean
TextView.get_iter_at_position (iter: struct.TextIterOUT, trailing: gint32OUTE, x: gint32, y: gint32): gboolean
Text.get_run_attributes (offset: gint32, start_offset: gint32OUTE, end_offset: gint32OUTE): List<void>
Text.get_selection (selection_num: gint32, start_offset: gint32OUTE, end_offset: gint32OUTE): utf8
Text.get_string_at_offset (offset: gint32, granularity: enum.TextGranularity, start_offset: gint32OUTE, end_offset: gint32OUTE): utf8
Text.get_text_after_offset (offset: gint32, boundary_type: enum.TextBoundary, start_offset: gint32OUTE, end_offset: gint32OUTE): utf8
Text.get_text_at_offset (offset: gint32, boundary_type: enum.TextBoundary, start_offset: gint32OUTE, end_offset: gint32OUTE): utf8
Text.get_text_before_offset (offset: gint32, boundary_type: enum.TextBoundary, start_offset: gint32OUTE, end_offset: gint32OUTE): utf8
ThemingEngine.has_region (style_region: utf8, flags: flags.RegionFlagsOUTE): gboolean
ThemingEngine.lookup_color (color_name: utf8, color: struct.RGBAOUT): gboolean
ThemingEngine.state_is_running (state: enum.StateType, progress: gdoubleOUTE): gboolean
TreeModelFilter.convert_child_iter_to_iter (filter_iter: struct.TreeIterOUT, child_iter: struct.TreeIter): gboolean
TreeModelSort.convert_child_iter_to_iter (sort_iter: struct.TreeIterOUT, child_iter: struct.TreeIter): gboolean
TreeModel.get_iter (iter: struct.TreeIterOUT, path: struct.TreePath): gboolean
TreeModel.get_iter_first (iter: struct.TreeIterOUT): gboolean
TreeModel.get_iter_from_string (iter: struct.TreeIterOUT, path_string: utf8): gboolean
TreeModel.iter_children (iter: struct.TreeIterOUT, parent: struct.TreeIter): gboolean
TreeModel.iter_nth_child (iter: struct.TreeIterOUT, parent: struct.TreeIter, n: gint32): gboolean
TreeModel.iter_parent (iter: struct.TreeIterOUT, child: struct.TreeIter): gboolean
TreePath.get_indices (depth: gint32OUTE): gint32[]
TreeSelection.get_selected (model: interface.TreeModelOUT, iter: struct.TreeIterOUT): gboolean
TreeSelection.get_selected_rows (model: interface.TreeModelOUT): List<struct.TreePath>
TreeSortable.get_sort_column_id (sort_column_id: gint32OUTE, order: enum.SortTypeOUTE): gboolean
TreeViewColumn.cell_get_position (cell_renderer: object.CellRenderer, x_offset: gint32OUTE, width: gint32OUTE): gboolean
TreeView.get_dest_row_at_pos (drag_x: gint32, drag_y: gint32, path: struct.TreePathOUTE, pos: enum.TreeViewDropPositionOUTE): gboolean
TreeView.get_path_at_pos (x: gint32, y: gint32, path: struct.TreePathOUTE, column: object.TreeViewColumnOUT, cell_x: gint32OUTE, cell_y: gint32OUTE): gboolean
TreeView.get_tooltip_context (x: gint32INOUTE, y: gint32INOUTE, keyboard_tip: gboolean, model: interface.TreeModelOUT, path: struct.TreePathOUTE, iter: struct.TreeIterOUT): gboolean
TreeView.get_visible_range (start_path: struct.TreePathOUTE, end_path: struct.TreePathOUTE): gboolean
TreeView.is_blank_at_pos (x: gint32, y: gint32, path: struct.TreePathOUTE, column: object.TreeViewColumnOUT, cell_x: gint32OUTE, cell_y: gint32OUTE): gboolean
WidgetClass.list_style_properties (n_properties: guint32OUTE): object.ParamSpec[]
WidgetPath.iter_has_qregion (pos: gint32, qname: guint32, flags: flags.RegionFlagsOUTE): gboolean
WidgetPath.iter_has_region (pos: gint32, name: utf8, flags: flags.RegionFlagsOUTE): gboolean
Widget.intersect (area: struct.Rectangle, intersection: struct.RectangleOUT): gboolean
Widget.translate_coordinates (dest_widget: object.Widget, src_x: gint32, src_y: gint32, dest_x: gint32OUTE, dest_y: gint32OUTE): gboolean
Window.get_resize_grip_area (rect: struct.RectangleOUT): gboolean
Gtk.get_current_event_state (state: flags.ModifierTypeOUTE): gboolean
Gtk.icon_size_lookup (size: gint32, width: gint32OUTE, height: gint32OUTE): gboolean
Gtk.icon_size_lookup_for_settings (settings: object.Settings, size: gint32, width: gint32OUTE, height: gint32OUTE): gboolean
Gtk.init_check (argc: gint32INOUTE, argv: utf8[]INOUTE): gboolean
Gtk.init_with_args (argc: gint32INOUTE, argv: utf8[]INOUTE, parameter_string: utf8, entries: struct.OptionEntry[], translation_domain: utf8): gboolean
Gtk.parse_args (argc: gint32INOUTE, argv: utf8[]INOUTE): gboolean
Gtk.rc_parse_color (scanner: struct.Scanner, color: struct.ColorOUT): guint32
Gtk.rc_parse_color_full (scanner: struct.Scanner, style: object.RcStyle, color: struct.ColorOUT): guint32
Gtk.rc_parse_state (scanner: struct.Scanner, state: enum.StateTypeOUTE): guint32
Gtk.stock_lookup (stock_id: utf8, item: struct.StockItemOUT): gboolean
Gtk.target_table_new_from_list (list: struct.TargetList, n_targets: gint32OUTE): struct.TargetEntry[]
Gtk.test_list_all_types (n_types: guint32OUTE): GType[]
Gtk.tree_get_row_drag_data (selection_data: struct.SelectionData, tree_model: interface.TreeModelOUT, path: struct.TreePathOUTE): gboolean
Yes, it works for me:
/*
* Gtk-3.0.js
*/
exports.apply = (Gtk) => {
console.log(Gtk.Widget) // => { [Function: GtkWidget] gtype: 63073824 }
process.exit(0)
}
How did you test it? I tried node ./run.js
/*
* run.js
*/
const gi = require('.')
const Gtk = gi.require('Gtk', '3.0')
I think I've messed up something, 'cause now I've tried again and it works indeed. Sorry for that
TBH, I remember seeing something similar to what you described, but I don't remember what I was testing or how I started the thing :) I think it was related to requiring inspect.js before index.js, or something.
Also: https://github.com/romgrk/node-gtk/blob/master/doc/overrides.md
@WebReflection This is one of the few remaining issues for the 1.0 release, I will probably start working on this at some point. If you do some work, be sure to git pull before, and git push fast.
Override gtk_css_provider_load_from_data
: convert string to uint8[]
We need to implement overrides on the JS side to expose a nice API for users. One such example is described here: https://github.com/romgrk/node-gtk/issues/4#issuecomment-400532978
Off the top of my head, the things that need to be overriden are:
[retval, out1, out2, ...]
For more inspiration, look at https://gitlab.gnome.org/GNOME/pygobject/blob/master/gi/overrides/Gtk.py. The file in which those overrides would be (for Gtk-3.0) is https://github.com/romgrk/node-gtk/blob/master/lib/overrides/Gtk-3.0.js.
ping @WebReflection