Skip to content

Commit

Permalink
Do not override the return type of all constructor functions automati…
Browse files Browse the repository at this point in the history
…cally.

For Gtk Widget constructors, override the return type with a GIR patch.

Fixes #160
  • Loading branch information
jwharm committed Jan 2, 2025
1 parent cd4fd41 commit 5e6dc20
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ private MethodSpec namedConstructor() {
MethodSpec.Builder builder = MethodSpec.methodBuilder(methodName)
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);

// Override the specified return type
TypeName returnType = parent.typeName();
TypeName returnType = ctor.returnValue().anyType().typeName();
builder.returns(returnType);

// Javadoc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public void setParent(Node parent) {
this.parent = parent;
}

public void setAttr(String key, String newValue) {
attributes.put(key, newValue);
}

public String attr(String key) {
return attributes.get(key);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,45 @@

import java.util.List;

import static java.util.function.Predicate.not;

public class GtkPatch implements Patch {

@Override
public GirElement patch(GirElement element, String namespace) {

/*
* Named constructors of Gtk Widgets often specify return type "Widget".
* To prevent redundant casts, we override them with the actual type.
*/
if (element instanceof Class cls) {
for (Constructor ctor : cls.constructors().stream()
.filter(not(f -> f.name().equals("new")))
.toList()) {
var type = (Type) ctor.returnValue().anyType();
if ("GtkWidget*".equals(type.cType())) {
if ("Gtk.Widget".equals(type.name())
|| ("Gtk".equals(namespace) && "Widget".equals(type.name()))) {
type.setAttr("name", cls.name());
type.setAttr("c:type", "Gtk" + cls.name() + "*");
}
}
}
}

if (!"Gtk".equals(namespace))
return element;

if (element instanceof Namespace ns) {

/*
* Gtk.CustomLayout is a convenience class for C code that wants to
* avoid subclassing Gtk.LayoutManager. It is not supposed to be used
* by language bindings, and will never work correctly, as it doesn't
* have the necessary parameters and annotations to manage the lifetime
* of the callback functions.
* See also https://github.com/gtk-rs/gtk4-rs/issues/23, especially the
* first comment.
* avoid subclassing Gtk.LayoutManager. It is not supposed to be
* used by language bindings, and will never work correctly, as it
* doesn't have the necessary parameters and annotations to manage
* the lifetime of the callback functions.
* See also https://github.com/gtk-rs/gtk4-rs/issues/23, especially
* the first comment.
*/
ns = remove(ns, Callback.class, "name", "CustomRequestModeFunc");
ns = remove(ns, Callback.class, "name", "CustomMeasureFunc");
Expand Down Expand Up @@ -72,35 +93,31 @@ public GirElement patch(GirElement element, String namespace) {
* different return type. Rename to getWindowId()
*/
if (element instanceof Method m
&& "gtk_application_window_get_id"
.equals(m.callableAttrs().cIdentifier()))
&& "gtk_application_window_get_id".equals(m.callableAttrs().cIdentifier()))
return m.withAttribute("name", "get_window_id");

/*
* MenuButton.getDirection() overrides Widget.getDirection() with a
* different return type. Rename to getArrowDirection()
*/
if (element instanceof Method m
&& "gtk_menu_button_get_direction"
.equals(m.callableAttrs().cIdentifier()))
&& "gtk_menu_button_get_direction".equals(m.callableAttrs().cIdentifier()))
return m.withAttribute("name", "get_arrow_direction");

/*
* PrintSettings.get() overrides GObject.get() with a different return
* type. Rename to getString()
*/
if (element instanceof Method m
&& "gtk_print_settings_get"
.equals(m.callableAttrs().cIdentifier()))
&& "gtk_print_settings_get".equals(m.callableAttrs().cIdentifier()))
return m.withAttribute("name", "get_string");

/*
* PrintUnixDialog.getSettings() overrides Widget.getSettings() with a
* different return type. Rename to getPrintSettings()
*/
if (element instanceof Method m
&& "gtk_print_unix_dialog_get_settings"
.equals(m.callableAttrs().cIdentifier()))
&& "gtk_print_unix_dialog_get_settings".equals(m.callableAttrs().cIdentifier()))
return m.withAttribute("name", "get_print_settings");

/*
Expand All @@ -110,8 +127,7 @@ public GirElement patch(GirElement element, String namespace) {
* Widget.activate() to activateWidget()
*/
if (element instanceof Method m
&& "gtk_widget_activate"
.equals(m.callableAttrs().cIdentifier()))
&& "gtk_widget_activate".equals(m.callableAttrs().cIdentifier()))
return m.withAttribute("name", "activate_widget");

/*
Expand All @@ -120,19 +136,18 @@ public GirElement patch(GirElement element, String namespace) {
* Class ApplicationWindow extends Widget and implements ActionGroup.
* This doesn't compile in Java. We rename Widget.activateAction()
* to activateActionIfExists() to resolve this.
*
* Furthermore, Widget.activateAction() is shadowed by
* Widget.activateActionVariant() so we have to rename the "shadows"
* attribute too.
*/
if (element instanceof Method m
&& "gtk_widget_activate_action"
.equals(m.callableAttrs().cIdentifier()))
&& "gtk_widget_activate_action".equals(m.callableAttrs().cIdentifier()))
return m.withAttribute("name", "activate_action_if_exists");

/*
* Furthermore, Widget.activateAction() is shadowed by
* Widget.activateActionVariant() so we have to rename the "shadows"
* attribute too.
*/
if (element instanceof Method m
&& "gtk_widget_activate_action_variant"
.equals(m.callableAttrs().cIdentifier()))
&& "gtk_widget_activate_action_variant".equals(m.callableAttrs().cIdentifier()))
return m.withAttribute("shadows", "activate_action_if_exists");

/*
Expand All @@ -142,9 +157,7 @@ public GirElement patch(GirElement element, String namespace) {
*/
if (element instanceof VirtualMethod vm
&& "play".equals(vm.name())
&& "MediaStream".equals(vm.parameters()
.instanceParameter()
.type().name()))
&& "MediaStream".equals(vm.parameters().instanceParameter().type().name()))
return vm.withAttribute("invoker", "play");

/*
Expand All @@ -153,20 +166,18 @@ public GirElement patch(GirElement element, String namespace) {
* override a public method with the same name in Widget. Therefore,
* they must also be public.
*/
var classes = List.of("Window", "Popover");
if (element instanceof VirtualMethod vm
&& "activate_default".equals(vm.name())
&& List.of("Window", "Popover").contains(
vm.parameters().instanceParameter().type().name()))
&& classes.contains(vm.parameters().instanceParameter().type().name()))
return vm.withAttribute("java-gi-override-visibility", "PUBLIC");

/*
* Same for Dialog.close() overriding Window.close()
*/
if (element instanceof VirtualMethod vm
&& "close".equals(vm.name())
&& "Dialog".equals(vm.parameters()
.instanceParameter()
.type().name()))
&& "Dialog".equals(vm.parameters().instanceParameter().type().name()))
return vm.withAttribute("java-gi-override-visibility", "PUBLIC");

/*
Expand All @@ -180,9 +191,7 @@ public GirElement patch(GirElement element, String namespace) {
);
if (element instanceof VirtualMethod vm
&& methods.contains(vm.name())
&& "BuilderScope".equals(vm.parameters()
.instanceParameter()
.type().name()))
&& "BuilderScope".equals(vm.parameters().instanceParameter().type().name()))
return vm.withAttribute("java-gi-dont-skip", "1");

/*
Expand All @@ -191,8 +200,7 @@ public GirElement patch(GirElement element, String namespace) {
* Make sure that it is the same on every platform.
*/
if (element instanceof Function f
&& "gtk_ordering_from_cmpfunc"
.equals(f.callableAttrs().cIdentifier()))
&& "gtk_ordering_from_cmpfunc".equals(f.callableAttrs().cIdentifier()))
return f.withAttribute("introspectable", "0");

/*
Expand Down Expand Up @@ -233,22 +241,34 @@ else if (element instanceof Method m
&& "gtk_multi_sorter_remove".equals(m.callableAttrs().cIdentifier()))
return element.withAttribute("name", "remove_at");

// MultiFilter implements ListModel<Filter> and supports mutation
if (element instanceof Class c && "MultiFilter".equals(c.name()))
return c.withAttribute("java-gi-generic-actual", "Gtk.Filter")
.withAttribute("java-gi-list-mutable", "1");

// AnyFilter implements ListModel<Filter>
if (element instanceof Class c && "AnyFilter".equals(c.name()))
return c.withAttribute("java-gi-generic-actual", "Gtk.Filter");

// EveryFilter implements ListModel<Filter>
if (element instanceof Class c && "EveryFilter".equals(c.name()))
return c.withAttribute("java-gi-generic-actual", "Gtk.Filter");

// MultiSorter implements ListModel<Sorter> and supports mutation
if (element instanceof Class c && "MultiSorter".equals(c.name()))
return c.withAttribute("java-gi-generic-actual", "Gtk.Sorter")
.withAttribute("java-gi-list-mutable", "1");

// StringList implements ListModel<StringObject> and supports splice
if (element instanceof Class c && "StringList".equals(c.name()))
return c.withAttribute("java-gi-generic-actual", "Gtk.StringObject")
.withAttribute("java-gi-list-spliceable", "1");

// Use StringObject.getString() for toString()
if (element instanceof Class i && "StringObject".equals(i.name()))
return i.withAttribute("java-gi-to-string", "getString()");

// Use StringFilter.getSearch() for toString()
if (element instanceof Class i && "StringFilter".equals(i.name()))
return i.withAttribute("java-gi-to-string", "getSearch()");

Expand Down

0 comments on commit 5e6dc20

Please sign in to comment.