diff --git a/examples/gio-2-action-entries/main.ts b/examples/gio-2-action-entries/main.ts index 5e598d6b..a95dfc3f 100644 --- a/examples/gio-2-action-entries/main.ts +++ b/examples/gio-2-action-entries/main.ts @@ -1,6 +1,14 @@ import Gio from 'gi://Gio'; import GLib from 'gi://GLib'; import GObject from 'gi://GObject'; +import System from 'system'; + +console.log('GJS Version:', System.version); + +if(System.version < 18200) { + console.log('GJS version 1.82.0 or higher is required for this example, skipping example'); + System.exit(0); +} export class ExampleApplication extends Gio.Application { static { diff --git a/examples/gio-2-iterate/main.ts b/examples/gio-2-iterate/main.ts index 193e56a2..0ac054a3 100644 --- a/examples/gio-2-iterate/main.ts +++ b/examples/gio-2-iterate/main.ts @@ -1,5 +1,13 @@ import GLib from 'gi://GLib' import Gio from 'gi://Gio' +import System from 'system'; + +console.log('GJS Version:', System.version); + +if(System.version < 18200) { + console.log('GJS version 1.82.0 or higher is required for this example, skipping example'); + System.exit(0); +} Gio._promisify(Gio.File.prototype, 'enumerate_children_async', 'enumerate_children_finish') diff --git a/examples/gobject-param-spec/main.ts b/examples/gobject-param-spec/main.ts new file mode 100644 index 00000000..6072bcda --- /dev/null +++ b/examples/gobject-param-spec/main.ts @@ -0,0 +1,121 @@ +import GObject from 'gi://GObject'; +import System from 'system'; + +console.log('GJS Version:', System.version); + +if(System.version < 18200) { + console.log('GJS version 1.82.0 or higher is required for nullable nick and blurb in GObject.ParamSpec, skipping example'); + System.exit(0); +} + +// Example class demonstrating different ParamSpec usages +class ExampleObject extends GObject.Object { + static { + GObject.registerClass({ + GTypeName: 'ExampleObject', + Properties: { + // Property with all fields + 'full-property': GObject.ParamSpec.string( + 'full-property', // name (required) + 'Full Property', // nick (optional) + 'A complete property', // blurb (optional) + GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, + 'default value' + ), + + // Property with null nick and blurb + 'minimal-property': GObject.ParamSpec.string( + 'minimal-property', + null, // nick can be null + null, // blurb can be null + GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, + '' + ), + + // Number property with null documentation + 'count': GObject.ParamSpec.int( + 'count', + null, // nick can be null + null, // blurb can be null + GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, + 0, // minimum + 100, // maximum + 0 // default value + ), + + // Boolean property with partial documentation + 'active': GObject.ParamSpec.boolean( + 'active', + 'Active', // providing nick + null, // but blurb can still be null + GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE, + false + ) + } + }, this); + } + + // Property values + private _fullProperty: string = 'default value'; + private _minimalProperty: string = ''; + private _count: number = 0; + private _active: boolean = false; + + // Property getters/setters + get full_property(): string { + return this._fullProperty; + } + + set full_property(value: string) { + this._fullProperty = value; + } + + get minimal_property(): string { + return this._minimalProperty; + } + + set minimal_property(value: string) { + this._minimalProperty = value; + } + + get count(): number { + return this._count; + } + + set count(value: number) { + this._count = value; + } + + get active(): boolean { + return this._active; + } + + set active(value: boolean) { + this._active = value; + } +} + +// Create and test the object +const obj = new ExampleObject(); + +// Test property access and type safety +obj.full_property = 'New Value'; +obj.minimal_property = 'Test'; +obj.count = 42; +obj.active = true; + +// Print current values +console.log('Full Property:', obj.full_property); +console.log('Minimal Property:', obj.minimal_property); +console.log('Count:', obj.count); +console.log('Active:', obj.active); + +// Get property info using GObject introspection +const properties = ExampleObject.list_properties(); + +console.log('\nProperty Information:'); +for (const pspec of properties) { + console.log(`\nProperty: ${pspec.get_name()}`); + console.log(`Nick: ${pspec.get_nick() || '(null)'}`); + console.log(`Blurb: ${pspec.get_blurb() || '(null)'}`); +} \ No newline at end of file diff --git a/examples/gobject-param-spec/package.json b/examples/gobject-param-spec/package.json new file mode 100644 index 00000000..2dc58d49 --- /dev/null +++ b/examples/gobject-param-spec/package.json @@ -0,0 +1,24 @@ +{ + "name": "@ts-for-gir-example/gobject-param-spec", + "version": "4.0.0-beta.19", + "description": "Example demonstrating GObject.ParamSpec with nullable fields", + "type": "module", + "private": true, + "scripts": { + "build:app": "tsc", + "build": "yarn build:app", + "start:app": "gjs -m dist/main.js", + "start": "yarn build && yarn start:app", + "validate": "yarn validate:types", + "validate:types": "tsc --noEmit", + "clear": "rm -rf dist" + }, + "devDependencies": { + "typescript": "^5.6.3" + }, + "dependencies": { + "@girs/gjs": "workspace:^", + "@girs/glib-2.0": "workspace:^", + "@girs/gobject-2.0": "workspace:^" + } +} diff --git a/examples/gobject-param-spec/tsconfig.json b/examples/gobject-param-spec/tsconfig.json new file mode 100644 index 00000000..934bd5f6 --- /dev/null +++ b/examples/gobject-param-spec/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "types": ["@girs/gjs", "@girs/gjs/dom", "@girs/gobject-2.0", "@girs/glib-2.0"], + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "outDir": "./dist" + }, + "files": [ + "main.ts" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 8d6991f4..5238d6d9 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "test:girs:packages": "yarn build:types:packages && yarn validate:types:packages", "test:girs:packages:gtk4": "yarn build:types:packages:gtk4 && yarn validate:types:packages", "test:examples": "yarn build:examples && yarn validate:examples && yarn test:examples:cli-apps", - "test:examples:cli-apps": "yarn workspace @ts-for-gir-example/glib-2-spawn-command-example run start && yarn workspace @ts-for-gir-example/gio-2-cat-example run start", + "test:examples:cli-apps": "yarn workspace @ts-for-gir-example/glib-2-spawn-command-example run start && yarn workspace @ts-for-gir-example/gio-2-cat-example run start && yarn workspace @ts-for-gir-example/gobject-param-spec run start && yarn workspace @ts-for-gir-example/gio-2-async run start && yarn workspace @ts-for-gir-example/gio-2-action-entries run start && yarn workspace @ts-for-gir-example/gio-2-iterate run start", "test:tests": "yarn workspaces foreach -v --all --parallel --include '@ts-for-gir-test/*' run test", "build": "yarn workspaces foreach --recursive --topological-dev --parallel --verbose --from @ts-for-gir/cli run build", "build:examples": "yarn workspaces foreach -v --all --include '@ts-for-gir-example/*' --parallel run build", diff --git a/packages/lib/src/injections/gobject.ts b/packages/lib/src/injections/gobject.ts index eebe69a7..cea9a7b7 100644 --- a/packages/lib/src/injections/gobject.ts +++ b/packages/lib/src/injections/gobject.ts @@ -30,20 +30,38 @@ import { IntrospectedField } from "../gir/property.js"; import { IntrospectedAlias } from "../gir/alias.js"; import { IntrospectedInterface } from "../gir/class.js"; -function typeParam(name: string, type: TypeExpression) { +function typeParam(name: string, type: TypeExpression, options: any = {}) { return new IntrospectedFunctionParameter({ name, direction: GirDirection.In, - type: type + type: type, + ...options }); } -function anyParam(name: string) { - return typeParam(name, AnyType); +function anyParam(name: string, options: any = {}) { + return typeParam(name, AnyType, options); } -function stringParam(name: string) { - return typeParam(name, StringType); +function stringParam(name: string, options: any = {}) { + return typeParam(name, StringType, options); +} + +/** + * Creates a parameter with a nullable string type. + * Used for optional documentation fields like 'nick' and 'blurb' in GObject properties. + * These fields are commonly used to provide human-readable descriptions of properties. + * + * @param name The parameter name + * @param options Additional options including documentation + * @returns An IntrospectedFunctionParameter with nullable string type + * @see https://gjs.guide/guides/gobject/basics.html#properties + */ +function nullableStringParam(name: string, options: any = {}) { + return typeParam(name, new NullableType(StringType), { + doc: "A nullable string parameter, commonly used for optional documentation fields in GObject properties", + ...options + }); } export default { @@ -95,8 +113,24 @@ export default { GType.noEmit(); const ParamSpec = namespace.assertClass("ParamSpec"); + ParamSpec.doc = `A GObject parameter specification that defines property characteristics. +See https://gjs.guide/guides/gobject/basics.html#properties for more details.`; const ParamFlags = namespace.getEnum("ParamFlags"); + /** + * Generates ParamSpec function definitions with proper typing for nullable documentation fields. + * Follows GJS style guidelines for property definitions with optional documentation. + * + * @param name The name of the ParamSpec type (e.g., "string", "int", "boolean") + * @param returnType The return type of the ParamSpec + * @param minMax Whether the ParamSpec has min/max values + * @param type Optional type parameter name + * @param defaultValue Whether the ParamSpec has a default value + * @param defaultValueType The type of the default value + * @param addGeneric Whether to add generic type parameters + * @returns An IntrospectedStaticClassFunction for the ParamSpec + * @see https://gjs.guide/guides/gjs/style-guide.html#properties + */ function generateParamSpec( name: string, returnType: TypeExpression = ParamSpec.getType(), @@ -104,29 +138,53 @@ export default { type: string | null = null, defaultValue = false, defaultValueType: TypeExpression = AnyType, - addGeneric = false + addGeneric = false, + doc: string ) { + const params = [ + stringParam("name", { + doc: "The name of the property" + }), + nullableStringParam("nick", { + doc: "A human readable name for the property" + }), + nullableStringParam("blurb", { + doc: "A longer description of the property" + }), + typeParam("flags", new BinaryType(ParamFlags?.getType() ?? AnyType, NumberType), { + doc: "The flags for this property (e.g. READABLE, WRITABLE)" + }) + ]; + + if (minMax) { + params.push(typeParam("minimum", NumberType, { + doc: "The minimum value for this property" + }), typeParam("maximum", NumberType, { + doc: "The maximum value for this property" + })); + } + + if (type) { + if (!addGeneric) { + params.push(anyParam(`${type}Type`)); + } else { + params.push(new IntrospectedFunctionParameter({ + name: `${type}Type`, + direction: GirDirection.In, + type: new NativeType("GType | { $gtype: GType }") + })); + } + } + + if (defaultValue) { + params.push(typeParam("defaultValue", defaultValueType, { + doc: "The default value for this property" + })); + } + const fn = new IntrospectedStaticClassFunction({ name, - parameters: [ - typeParam("name", StringType), - typeParam("nick", StringType), - typeParam("blurb", StringType), - typeParam("flags", new BinaryType(ParamFlags?.getType() ?? AnyType, NumberType)), - ...(minMax ? [typeParam("minimum", NumberType), typeParam("maximum", NumberType)] : []), - ...(type - ? !addGeneric - ? [anyParam(`${type}Type`)] - : [ - new IntrospectedFunctionParameter({ - name: `${type}Type`, - direction: GirDirection.In, - type: new NativeType("GType | { $gtype: GType }") - }) - ] - : []), - ...(defaultValue ? [typeParam("defaultValue", defaultValueType)] : []) - ], + parameters: params, parent: ParamSpec, return_type: returnType }); @@ -135,6 +193,8 @@ export default { fn.generics.push(new Generic(new GenericType("T"))); } + fn.doc = doc; + return fn; } @@ -172,8 +232,8 @@ export default { name: "object", parameters: [ stringParam("name"), - stringParam("nick"), - stringParam("blurb"), + nullableStringParam("nick"), + nullableStringParam("blurb"), stringParam("flags"), new IntrospectedFunctionParameter({ name: "objectType", @@ -190,7 +250,7 @@ export default { // static jsobject(name: string, nick: string, blurb: string, flags: ParamFlags): ParamSpecBoxed const jsobject = new IntrospectedStaticClassFunction({ name: "jsobject", - parameters: [stringParam("name"), stringParam("nick"), stringParam("blurb"), anyParam("flags")], + parameters: [stringParam("name"), nullableStringParam("nick"), nullableStringParam("blurb"), anyParam("flags")], parent: ParamSpec, return_type: new NativeType("ParamSpec") }); @@ -224,56 +284,55 @@ export default { } ParamSpec.members.push( - // "char": "static char(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("char", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "uchar": "static uchar(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any):ParamSpec;", - generateParamSpec("uchar", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "int": "static int(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("int", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "uint": "static uint(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("uint", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "long": "static long(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("long", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "ulong": "static ulong(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("ulong", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "int64": "static int64(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("int64", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "uint64": "static uint64(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("uint64", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "float": "static float(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("float", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "boolean": "static boolean(name: any, nick: any, blurb: any, flags: any, defaultValue: any): ParamSpec;", - generateParamSpec("boolean", ParamSpecWithGenerics(BooleanType), false, null, true, BooleanType), - // "flags": "static flags(name: any, nick: any, blurb: any, flags: any, flagsType: any, defaultValue: any): ParamSpec;", - generateParamSpec("flags", ParamSpecWithGenerics(NumberType), false, "flags", true), - // "enum": "static enum(name: any, nick: any, blurb: any, flags: any, enumType: any, defaultValue: any): ParamSpec;", - generateParamSpec( - "enum", - ParamSpecWithGenerics(new NativeType("T")), - false, - "enum", - true, - undefined, - true + generateParamSpec("char", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecChar instance specifying a G_TYPE_CHAR property." + ), + generateParamSpec("uchar", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecUChar instance specifying a G_TYPE_UCHAR property." + ), + generateParamSpec("int", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecInt instance specifying a G_TYPE_INT property." + ), + generateParamSpec("uint", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecUInt instance specifying a G_TYPE_UINT property." + ), + generateParamSpec("long", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecLong instance specifying a G_TYPE_LONG property." + ), + generateParamSpec("ulong", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecULong instance specifying a G_TYPE_ULONG property." + ), + generateParamSpec("int64", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecInt64 instance specifying a G_TYPE_INT64 property." + ), + generateParamSpec("uint64", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecUInt64 instance specifying a G_TYPE_UINT64 property." ), - // "double": "static double(name: any, nick: any, blurb: any, flags: any, minimum: any, maximum: any, defaultValue: any): ParamSpec;", - generateParamSpec("double", ParamSpecWithGenerics(NumberType), true, null, true, NumberType), - // "string": "static string(name: any, nick: any, blurb: any, flags: any, defaultValue: any): ParamSpec;", - generateParamSpec("string", ParamSpecWithGenerics(StringType), false, null, true, StringType), - // "boxed": "static boxed(name: any, nick: any, blurb: any, flags: any, boxedType: any): ParamSpec;", - generateParamSpec( - "boxed", - ParamSpecWithGenerics(new NativeType("T")), - false, - "boxed", - false, - undefined, - true + generateParamSpec("float", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecFloat instance specifying a G_TYPE_FLOAT property." + ), + generateParamSpec("boolean", ParamSpecWithGenerics(BooleanType), false, null, true, BooleanType, false, + "Creates a new GParamSpecBoolean instance specifying a G_TYPE_BOOLEAN property. In many cases, it may be more appropriate to use an enum with g_param_spec_enum(), both to improve code clarity by using explicitly named values, and to allow for more values to be added in future without breaking API." + ), + generateParamSpec("flags", ParamSpecWithGenerics(NumberType), false, "flags", true, undefined, false, + "Creates a new GParamSpecFlags instance specifying a G_TYPE_FLAGS property." + ), + generateParamSpec("enum", ParamSpecWithGenerics(new NativeType("T")), false, "enum", true, undefined, true, + "Creates a new GParamSpecEnum instance specifying a G_TYPE_ENUM property." + ), + generateParamSpec("double", ParamSpecWithGenerics(NumberType), true, null, true, NumberType, false, + "Creates a new GParamSpecDouble instance specifying a G_TYPE_DOUBLE property." + ), + generateParamSpec("string", ParamSpecWithGenerics(StringType), false, null, true, StringType, false, + "Creates a new GParamSpecString instance specifying a G_TYPE_STRING property." + ), + generateParamSpec("boxed", ParamSpecWithGenerics(new NativeType("T")), false, "boxed", false, undefined, true, + "Creates a new GParamSpecBoxed instance specifying a G_TYPE_BOXED derived property." ), - // "object": "static object(name: any, nick: any, blurb: any, flags: any, objectType: any): ParamSpec;", object, - // "param": "static param(name: any, nick: any, blurb: any, flags: any, paramType: any): ParamSpec;", - generateParamSpec("param", ParamSpec.getType(), false, "param", false), + generateParamSpec("param", ParamSpec.getType(), false, "param", false, undefined, false, + "Creates a new GParamSpecParam instance specifying a G_TYPE_PARAM property." + ), jsobject, override ); diff --git a/yarn.lock b/yarn.lock index 2de511fd..c81eb6cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12832,6 +12832,17 @@ __metadata: languageName: unknown linkType: soft +"@ts-for-gir-example/gobject-param-spec@workspace:examples/gobject-param-spec": + version: 0.0.0-use.local + resolution: "@ts-for-gir-example/gobject-param-spec@workspace:examples/gobject-param-spec" + dependencies: + "@girs/gjs": "workspace:^" + "@girs/glib-2.0": "workspace:^" + "@girs/gobject-2.0": "workspace:^" + typescript: "npm:^5.6.3" + languageName: unknown + linkType: soft + "@ts-for-gir-example/gtk-3-browser-example@workspace:examples/gtk-3-browser": version: 0.0.0-use.local resolution: "@ts-for-gir-example/gtk-3-browser-example@workspace:examples/gtk-3-browser"