diff --git a/bindings/src/index.ts b/bindings/src/index.ts index 501996e..e4cca19 100644 --- a/bindings/src/index.ts +++ b/bindings/src/index.ts @@ -360,6 +360,7 @@ function main(){ getFunction(api.functions, "SaveFileData")!.binding = { } ignore("ExportDataAsCode") getFunction(api.functions, "LoadFileText")!.binding = { after: gen => gen.call("UnloadFileText", ["returnVal"]) } + getFunction(api.functions, "SaveFileText")!.params![1].binding = { typeAlias: "const char *" } ignore("UnloadFileText") const createFileList = (gen: QuickJsGenerator, loadName: string, unloadName: string, args: string[]) => { @@ -496,14 +497,30 @@ function main(){ core.exportGlobalConstant("DEG2RAD", "(PI/180.0)") core.exportGlobalConstant("RAD2DEG", "(180.0/PI)") - ignore("GuiDropdownBox") - ignore("GuiSpinner") - ignore("GuiValueBox") - ignore("GuiListView") + const setOutParam = (fun: RayLibFunction, index: number) => { + const param = fun!.params![index] + param.binding = { + jsType: `{ ${param.name}: number }`, + customConverter: gen => { + gen.declare(param.name+"_out", param.type.replace(" *","")) + gen.declare(param.name, param.type, false, "&"+param.name+"_out") + }, + customCleanup: gen => { + gen.call("JS_SetPropertyStr", ["ctx", "argv["+index+"]", `"${param.name}"`, "JS_NewInt32(ctx,"+param.name+"_out)"]) + } + } + } + + setOutParam(getFunction(api.functions, "GuiDropdownBox")!, 2) + setOutParam(getFunction(api.functions, "GuiSpinner")!, 2) + setOutParam(getFunction(api.functions, "GuiValueBox")!, 2) + setOutParam(getFunction(api.functions, "GuiListView")!, 2) ignore("GuiListViewEx") + ignore("GuiTextBox") ignore("GuiTextInputBox") - ignore("GuiGetIcons") + //setOutParam(getFunction(api.functions, "GuiTextInputBox")!, 6) ignore("GuiTabBar") + ignore("GuiGetIcons") ignore("GuiLoadIcons") // TODO: Parse and support light struct ignore("CreateLight") diff --git a/bindings/src/interfaces.ts b/bindings/src/interfaces.ts index 4b116c8..35e0dc9 100644 --- a/bindings/src/interfaces.ts +++ b/bindings/src/interfaces.ts @@ -20,7 +20,11 @@ export interface FuncBindingOptions { } export interface ParamBindingOptions { - ignore?: boolean + ignore?: boolean, + customConverter?: (gen: QuickJsGenerator) => void, + customCleanup?: (gen: QuickJsGenerator) => void, + jsType?: string, + typeAlias?: string } export type RayLibType = "void" | "const char *" | "bool" | "float" | "unsigned char" | "void *" | "int" | "usigned int" | "Texture" | "Rectangle" | "Image" | "Rectangle *" | "GylphInfo *" | "Texture2D" | "Vector3" | "Vector2" | "float *" | "unsigned char *" | "unsigned short *" | "unsigned int *" | "Shader" | "MaterialMap *" | "float[4]" | "Vector3" diff --git a/bindings/src/quickjs.ts b/bindings/src/quickjs.ts index cf2be79..36c9687 100644 --- a/bindings/src/quickjs.ts +++ b/bindings/src/quickjs.ts @@ -1,5 +1,4 @@ import { writeFileSync } from "fs"; -import { ApiFunction } from "./api" import { CodeGenerator, CodeWriter, FunctionArgument, GenericCodeGenerator } from "./generation" export type StructLookup = { [struct: string]: string } @@ -76,8 +75,8 @@ export abstract class GenericQuickJsGenerator extend return sub } - jsToC(type: string, name: string, src: string, classIds: StructLookup = {}, supressDeclaration = false){ - switch (type) { + jsToC(type: string, name: string, src: string, classIds: StructLookup = {}, supressDeclaration = false, typeAlias?: string){ + switch (typeAlias ?? type) { // Array Buffer case "const void *": case "void *": @@ -93,7 +92,7 @@ export abstract class GenericQuickJsGenerator extend break; // String case "const char *": - case "char *": + //case "char *": if(!supressDeclaration) this.statement(`${type} ${name} = (${type})JS_ToCString(ctx, ${src})`) else this.statement(`${name} = (${type})JS_ToCString(ctx, ${src})`) break; @@ -125,7 +124,6 @@ export abstract class GenericQuickJsGenerator extend if(!supressDeclaration) this.statement(`${type} ${name} = JS_ToBool(ctx, ${src})`) else this.statement(`${name} = JS_ToBool(ctx, ${src})`) break; - // Structs / Struct * default: const isConst = type.startsWith('const') const isPointer = type.endsWith(' *') diff --git a/bindings/src/raylib-header.ts b/bindings/src/raylib-header.ts index 3a77ee5..a12fdaa 100644 --- a/bindings/src/raylib-header.ts +++ b/bindings/src/raylib-header.ts @@ -34,13 +34,15 @@ export class RayLibHeader extends QuickJsHeader { for (let i = 0; i < api.params.length; i++) { if(api.params[i]?.binding?.ignore) continue; const para = api.params[i] - fun.jsToC(para.type,para.name,"argv["+i+"]", this.structLookup) + if(para.binding?.customConverter) para.binding.customConverter(fun) + else fun.jsToC(para.type,para.name,"argv["+i+"]", this.structLookup, false, para.binding?.typeAlias) } // call c function if(options.customizeCall) fun.line(options.customizeCall) else fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : {type: api.returnType, name: "returnVal"}) // clean up parameters for (const param of api.params) { + if(param.binding?.customCleanup) param.binding.customCleanup(fun) fun.jsCleanUpParameter(param.type, param.name) } // return result diff --git a/bindings/src/typescript.ts b/bindings/src/typescript.ts index 05e34c0..93acee6 100644 --- a/bindings/src/typescript.ts +++ b/bindings/src/typescript.ts @@ -18,7 +18,7 @@ export class TypeScriptDeclaration { addFunction(name: string, api: RayLibFunction){ const options = api.binding || {} - const para = (api.params || []).filter(x => !x.binding?.ignore).map(x => ({ name: x.name, type: this.toJsType(x.type)})) + const para = (api.params || []).filter(x => !x.binding?.ignore).map(x => ({ name: x.name, type: x.binding?.jsType ?? this.toJsType(x.type)})) const returnType = options.jsReturns ?? this.toJsType(api.returnType) this.functions.tsDeclareFunction(name, para, returnType, api.description) } diff --git a/examples/lib.raylib.d.ts b/examples/lib.raylib.d.ts index da35b7f..91786c2 100644 --- a/examples/lib.raylib.d.ts +++ b/examples/lib.raylib.d.ts @@ -1478,8 +1478,12 @@ declare function guiToggleGroup(bounds: Rectangle, text: string, active: number) declare function guiCheckBox(bounds: Rectangle, text: string, checked: boolean): boolean; /** Combo Box control, returns selected item index */ declare function guiComboBox(bounds: Rectangle, text: string, active: number): number; -/** Text Box control, updates input text */ -declare function guiTextBox(bounds: Rectangle, text: string, textSize: number, editMode: boolean): boolean; +/** Dropdown Box control, returns selected item */ +declare function guiDropdownBox(bounds: Rectangle, text: string, active: { active: number }, editMode: boolean): boolean; +/** Spinner control, returns selected value */ +declare function guiSpinner(bounds: Rectangle, text: string, value: { value: number }, minValue: number, maxValue: number, editMode: boolean): boolean; +/** Value Box control, updates input text with numbers */ +declare function guiValueBox(bounds: Rectangle, text: string, value: { value: number }, minValue: number, maxValue: number, editMode: boolean): boolean; /** Slider control, returns selected value */ declare function guiSlider(bounds: Rectangle, textLeft: string, textRight: string, value: number, minValue: number, maxValue: number): number; /** Slider Bar control, returns selected value */ @@ -1492,6 +1496,8 @@ declare function guiStatusBar(bounds: Rectangle, text: string): void; declare function guiDummyRec(bounds: Rectangle, text: string): void; /** Grid control, returns mouse cell position */ declare function guiGrid(bounds: Rectangle, text: string, spacing: number, subdivs: number): Vector2; +/** List View control, returns selected list item index */ +declare function guiListView(bounds: Rectangle, text: string, scrollIndex: { scrollIndex: number }, active: number): number; /** Message Box control, displays a message */ declare function guiMessageBox(bounds: Rectangle, title: string, message: string, buttons: string): number; /** Color Picker control (multiple color controls) */ diff --git a/examples/raygui/controls_test_suite.js b/examples/raygui/controls_test_suite.js index d1f2304..f15a0d9 100644 --- a/examples/raygui/controls_test_suite.js +++ b/examples/raygui/controls_test_suite.js @@ -1,55 +1,29 @@ -/******************************************************************************************* -* -* raygui - controls test suite -* -* TEST CONTROLS: -* - GuiDropdownBox() -* - GuiCheckBox() -* - GuiSpinner() -* - GuiValueBox() -* - GuiTextBox() -* - GuiButton() -* - GuiComboBox() -* - GuiListView() -* - GuiToggleGroup() -* - GuiColorPicker() -* - GuiSlider() -* - GuiSliderBar() -* - GuiProgressBar() -* - GuiColorBarAlpha() -* - GuiScrollPanel() -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2016-2023 Ramon Santamaria (@raysan5) -* -**********************************************************************************************/ // Initialization -//-------------------------------------------------------------------------------------- -const screenWidth = 690; -const screenHeight = 560; +//--------------------------------------------------------------------------------------- +let screenWidth = 690; +let screenHeight = 560; initWindow(screenWidth, screenHeight, "raygui - controls test suite"); setExitKey(0); // GUI controls initialization //---------------------------------------------------------------------------------- -let dropdownBox000Active = 0; +let dropdownBox000Active = {active: 0}; let dropDown000EditMode = false; -let dropdownBox001Active = 0; +let dropdownBox001Active = {active:0}; let dropDown001EditMode = false; -let spinner001Value = 0; +let spinner001Value = { value: 0 }; let spinnerEditMode = false; -let valueBox002Value = 0; +let valueBox002Value = { value: 0 }; let valueBoxEditMode = false; let textBoxText = "Text box"; let textBoxEditMode = false; -let listViewScrollIndex = 0; +let listViewScrollIndex = { scrollIndex: 0 }; let listViewActive = -1; let listViewExScrollIndex = 0; @@ -61,7 +35,7 @@ let multiTextBoxText = "Multi text box"; let multiTextBoxEditMode = false; let colorPickerValue = RED; -let sliderValue = 50.0; +let sliderValue = 50; let sliderBarValue = 60; let progressValue = 0.4; @@ -73,20 +47,20 @@ let comboBoxActive = 1; let toggleGroupActive = 0; -let viewScroll = new Vector2(0, 0); +let viewScroll = new Vector2(0,0); //---------------------------------------------------------------------------------- // Custom GUI font loading -//Font font = LoadFontEx("fonts/rainyhearts16.ttf", 12, 0, 0); -//GuiSetFont(font); +//let font = loadFontEx("fonts/rainyhearts16.ttf", 12, 0, 0); +//guiSetFont(font); let exitWindow = false; let showMessageBox = false; -let textInput = new Array(256).fill(0); +let textInput = ""; let showTextInputBox = false; -let textInputFileName = new Array(256).fill(0); +let textInputFileName = ""; setTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -104,9 +78,9 @@ while (!exitWindow) // Detect window close button or ESC key if (isFileDropped()) { - const droppedFiles = loadDroppedFiles(); + let droppedFiles = loadDroppedFiles(); - if ((droppedFiles.length > 0) && isFileExtension(droppedFiles[0], ".rgs")) guiLoadStyle(droppedFiles.paths[0]); + if ((droppedFiles.length > 0) && isFileExtension(droppedFiles[0], ".rgs")) guiLoadStyle(droppedFiles[0]); } //---------------------------------------------------------------------------------- @@ -123,99 +97,97 @@ while (!exitWindow) // Detect window close button or ESC key dropDown001EditMode) guiLock(); // First GUI column - guiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); - forceSquareChecked = guiCheckBox(new Rectangle(25, 108, 15, 15), "FORCE CHECK!", forceSquaredChecked); + //guiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + forceSquaredChecked = guiCheckBox(new Rectangle(25, 108, 15, 15), "FORCE CHECK!", forceSquaredChecked); guiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); - guiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); - if (guiSpinner(new Rectangle(25, 135, 125, 30), NULL, &spinner001Value, 0, 100, spinnerEditMode)) spinnerEditMode = !spinnerEditMode; - //if (guiValueBox(new Rectangle(25, 175, 125, 30), NULL, &valueBox002Value, 0, 100, valueBoxEditMode)) valueBoxEditMode = !valueBoxEditMode; + //guiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + if (guiSpinner(new Rectangle(25, 135, 125, 30), null, spinner001Value, 0, 100, spinnerEditMode)) spinnerEditMode = !spinnerEditMode; + if (guiValueBox(new Rectangle(25, 175, 125, 30), null, valueBox002Value, 0, 100, valueBoxEditMode)) valueBoxEditMode = !valueBoxEditMode; guiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); - if (guiTextBox(new Rectangle(25, 215, 125, 30), textBoxText, 64, textBoxEditMode)) textBoxEditMode = !textBoxEditMode; + //if (guiTextBox(new Rectangle(25, 215, 125, 30), textBoxText, 64, textBoxEditMode)) textBoxEditMode = !textBoxEditMode; guiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); - if (guiButton(new Rectangle( 25, 255, 125, 30 ), guiIconText(ICON_FILE_SAVE, "Save File"))) showTextInputBox = true; + if (guiButton(new Rectangle(25, 255, 125, 30), guiIconText(ICON_FILE_SAVE, "Save File"))) showTextInputBox = true; - guiGroupBox(Rectangle( 25, 310, 125, 150 ), "STATES"); - //GuiLock(); + guiGroupBox(new Rectangle(25, 310, 125, 150), "STATES"); + //guiLock(); guiSetState(STATE_NORMAL); if (guiButton(new Rectangle(30, 320, 115, 30), "NORMAL")) { } guiSetState(STATE_FOCUSED); if (guiButton(new Rectangle(30, 355, 115, 30), "FOCUSED")) { } guiSetState(STATE_PRESSED); if (guiButton(new Rectangle(30, 390, 115, 30), "#15#PRESSED")) { } guiSetState(STATE_DISABLED); if (guiButton(new Rectangle(30, 425, 115, 30), "DISABLED")) { } guiSetState(STATE_NORMAL); - //GuiUnlock(); + //guiUnlock(); comboBoxActive = guiComboBox(new Rectangle(25, 470, 125, 30), "ONE;TWO;THREE;FOUR", comboBoxActive); // NOTE: GuiDropdownBox must draw after any other control that can be covered on unfolding - GuiUnlock(); - GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); - if (GuiDropdownBox(new Rectangle(25, 65, 125, 30), "#01#ONE;#02#TWO;#03#THREE;#04#FOUR", &dropdownBox001Active, dropDown001EditMode)) dropDown001EditMode = !dropDown001EditMode; + guiUnlock(); + guiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + if (guiDropdownBox(new Rectangle(25, 65, 125, 30), "#01#ONE;#02#TWO;#03#THREE;#04#FOUR", dropdownBox001Active, dropDown001EditMode)) dropDown001EditMode = !dropDown001EditMode; - GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); - if (GuiDropdownBox(new Rectangle(25, 25, 125, 30), "ONE;TWO;THREE", &dropdownBox000Active, dropDown000EditMode)) dropDown000EditMode = !dropDown000EditMode; + guiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + if (guiDropdownBox(new Rectangle(25, 25, 125, 30), "ONE;TWO;THREE", dropdownBox000Active, dropDown000EditMode)) dropDown000EditMode = !dropDown000EditMode; // Second GUI column - GuiListView(new Rectangle(165, 25, 140, 140), "Charmander;Bulbasaur;#18#Squirtel;Pikachu;Eevee;Pidgey", &listViewScrollIndex, &listViewActive); - GuiListViewEx(new Rectangle(165, 180, 140, 200), listViewExList, 8, &listViewExScrollIndex, &listViewExActive, &listViewExFocus); + listViewActive = guiListView(new Rectangle(165, 25, 140, 140), "Charmander;Bulbasaur;#18#Squirtel;Pikachu;Eevee;Pidgey", listViewScrollIndex, listViewActive); + //listViewExActive = guiListViewEx(new Rectangle(165, 180, 140, 200), listViewExList, 8, &listViewExFocus, &listViewExScrollIndex, listViewExActive); - //GuiToggle(new Rectangle(165, 400, 140, 25), "#1#ONE", &toggleGroupActive); - GuiToggleGroup(new Rectangle(165, 400, 140, 25), "#1#ONE\n#3#TWO\n#8#THREE\n#23#", &toggleGroupActive); + toggleGroupActive = guiToggleGroup(new Rectangle(165, 400, 140, 25), "#1#ONE\n#3#TWO\n#8#THREE\n#23#", toggleGroupActive); // Third GUI column - GuiPanel(new Rectangle(320, 25, 225, 140), "Panel Info"); - GuiColorPicker(new Rectangle(320, 185, 196, 192), NULL, &colorPickerValue); + guiPanel(new Rectangle(320, 25, 225, 140), "Panel Info"); + colorPickerValue = guiColorPicker(new Rectangle(320, 185, 196, 192), null, colorPickerValue); - GuiSlider(new Rectangle(355, 400, 165, 20), "TEST", TextFormat("%2.2f", sliderValue), &sliderValue, -50, 100); - GuiSliderBar(new Rectangle(320, 430, 200, 20), NULL, TextFormat("%i", (int)sliderBarValue), &sliderBarValue, 0, 100); - GuiProgressBar(new Rectangle(320, 460, 200, 20), NULL, NULL, &progressValue, 0, 1); + sliderValue = guiSlider(new Rectangle(355, 400, 165, 20), "TEST", sliderValue.toString(), sliderValue, -50, 100); + sliderBarValue = guiSliderBar(new Rectangle(320, 430, 200, 20), null, sliderBarValue.toString(), sliderBarValue, 0, 100); + progressValue = guiProgressBar(new Rectangle(320, 460, 200, 20), null, null, progressValue, 0, 1); // NOTE: View rectangle could be used to perform some scissor test - Rectangle view = { 0 }; - GuiScrollPanel(new Rectangle(560, 25, 102, 354), NULL, new Rectangle(560, 25, 300, 1200), &viewScroll, &view); + let view = guiScrollPanel(new Rectangle(560, 25, 102, 354), null, new Rectangle(560, 25, 300, 1200), viewScroll); - Vector2 mouseCell = { 0 }; - GuiGrid((Rectangle) { 560, 25 + 180 + 195, 100, 120 }, NULL, 20, 2, &mouseCell); + guiGrid(new Rectangle(560, 25 + 180 + 195, 100, 120), null, 20, 2); - GuiStatusBar(new Rectangle(0, (float)GetScreenHeight() - 20, (float)GetScreenWidth(), 20), "This is a status bar"); + guiStatusBar(new Rectangle(0, getScreenHeight() - 20, getScreenWidth(), 20), "This is a status bar"); - GuiColorBarAlpha(new Rectangle(320, 490, 200, 30), NULL, &alphaValue); + alphaValue = guiColorBarAlpha(new Rectangle(320, 490, 200, 30), null, alphaValue); if (showMessageBox) { - DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, 0.8f)); - int result = GuiMessageBox(new Rectangle((float)GetScreenWidth()/2 - 125, (float)GetScreenHeight()/2 - 50, 250, 100), GuiIconText(ICON_EXIT, "Close Window"), "Do you really want to exit?", "Yes;No"); + drawRectangle(0, 0, getScreenWidth(), getScreenHeight(), fade(RAYWHITE, 0.8)); + let result = guiMessageBox(new Rectangle(getScreenWidth()/2 - 125, getScreenHeight()/2 - 50, 250, 100), guiIconText(ICON_EXIT, "Close Window"), "Do you really want to exit?", "Yes;No"); if ((result == 0) || (result == 2)) showMessageBox = false; else if (result == 1) exitWindow = true; } - if (showTextInputBox) - { - DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, 0.8f)); - int result = GuiTextInputBox(new Rectangle((float)GetScreenWidth()/2 - 120, (float)GetScreenHeight()/2 - 60, 240, 140), "Save", GuiIconText(ICON_FILE_SAVE, "Save file as..."), "Ok;Cancel", textInput, 255, NULL); + // if (showTextInputBox) + // { + // drawRectangle(0, 0, getScreenWidth(), getScreenHeight(), fade(RAYWHITE, 0.8)); + // let result = guiTextInputBox(new Rectangle(getScreenWidth()/2 - 120, getScreenHeight()/2 - 60, 240, 140), "Save", guiIconText(ICON_FILE_SAVE, "Save file as..."), "Ok;Cancel", textInput, 255, null); - if (result == 1) - { - // TODO: Validate textInput value and save + // if (result == 1) + // { + // // TODO: Validate textInput value and save - strcpy(textInputFileName, textInput); - } + // strcpy(textInputFileName, textInput); + // } - if ((result == 0) || (result == 1) || (result == 2)) - { - showTextInputBox = false; - strcpy(textInput, "\0"); - } - } + // if ((result == 0) || (result == 1) || (result == 2)) + // { + // showTextInputBox = false; + // strcpy(textInput, "\0"); + // } + // } //---------------------------------------------------------------------------------- - EndDrawing(); + endDrawing(); //---------------------------------------------------------------------------------- } // De-Initialization //-------------------------------------------------------------------------------------- -CloseWindow(); // Close window and OpenGL context -//-------------------------------------------------------------------------------------- \ No newline at end of file +closeWindow(); // Close window and OpenGL context +//-------------------------------------------------------------------------------------- + diff --git a/generate-bindings.js b/generate-bindings.js index ec6eec6..6b4ed21 100644 --- a/generate-bindings.js +++ b/generate-bindings.js @@ -403,8 +403,8 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator { const sub = this.function("js_" + jsName, "JSValue", args, true); return sub; } - jsToC(type, name, src, classIds = {}, supressDeclaration = false) { - switch (type) { + jsToC(type, name, src, classIds = {}, supressDeclaration = false, typeAlias) { + switch (typeAlias ?? type) { // Array Buffer case "const void *": case "void *": @@ -420,7 +420,7 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator { break; // String case "const char *": - case "char *": + //case "char *": if (!supressDeclaration) this.statement(`${type} ${name} = (${type})JS_ToCString(ctx, ${src})`); else @@ -463,7 +463,6 @@ class GenericQuickJsGenerator extends generation_1.GenericCodeGenerator { else this.statement(`${name} = JS_ToBool(ctx, ${src})`); break; - // Structs / Struct * default: const isConst = type.startsWith('const'); const isPointer = type.endsWith(' *'); @@ -665,7 +664,10 @@ class RayLibHeader extends quickjs_1.QuickJsHeader { if (api.params[i]?.binding?.ignore) continue; const para = api.params[i]; - fun.jsToC(para.type, para.name, "argv[" + i + "]", this.structLookup); + if (para.binding?.customConverter) + para.binding.customConverter(fun); + else + fun.jsToC(para.type, para.name, "argv[" + i + "]", this.structLookup, false, para.binding?.typeAlias); } // call c function if (options.customizeCall) @@ -674,6 +676,8 @@ class RayLibHeader extends quickjs_1.QuickJsHeader { fun.call(api.name, api.params.map(x => x.name), api.returnType === "void" ? null : { type: api.returnType, name: "returnVal" }); // clean up parameters for (const param of api.params) { + if (param.binding?.customCleanup) + param.binding.customCleanup(fun); fun.jsCleanUpParameter(param.type, param.name); } // return result @@ -774,7 +778,7 @@ class TypeScriptDeclaration { } addFunction(name, api) { const options = api.binding || {}; - const para = (api.params || []).filter(x => !x.binding?.ignore).map(x => ({ name: x.name, type: this.toJsType(x.type) })); + const para = (api.params || []).filter(x => !x.binding?.ignore).map(x => ({ name: x.name, type: x.binding?.jsType ?? this.toJsType(x.type) })); const returnType = options.jsReturns ?? this.toJsType(api.returnType); this.functions.tsDeclareFunction(name, para, returnType, api.description); } @@ -1253,6 +1257,7 @@ function main() { getFunction(api.functions, "SaveFileData").binding = {}; ignore("ExportDataAsCode"); getFunction(api.functions, "LoadFileText").binding = { after: gen => gen.call("UnloadFileText", ["returnVal"]) }; + getFunction(api.functions, "SaveFileText").params[1].binding = { typeAlias: "const char *" }; ignore("UnloadFileText"); const createFileList = (gen, loadName, unloadName, args) => { gen.call(loadName, args, { type: "FilePathList", name: "files" }); @@ -1377,14 +1382,29 @@ function main() { ignore("QuaternionToAxisAngle"); core.exportGlobalConstant("DEG2RAD", "(PI/180.0)"); core.exportGlobalConstant("RAD2DEG", "(180.0/PI)"); - ignore("GuiDropdownBox"); - ignore("GuiSpinner"); - ignore("GuiValueBox"); - ignore("GuiListView"); + const setOutParam = (fun, index) => { + const param = fun.params[index]; + param.binding = { + jsType: `{ ${param.name}: number }`, + customConverter: gen => { + gen.declare(param.name + "_out", param.type.replace(" *", "")); + gen.declare(param.name, param.type, false, "&" + param.name + "_out"); + }, + customCleanup: gen => { + gen.call("JS_SetPropertyStr", ["ctx", "argv[" + index + "]", `"${param.name}"`, "JS_NewInt32(ctx," + param.name + "_out)"]); + } + }; + }; + setOutParam(getFunction(api.functions, "GuiDropdownBox"), 2); + setOutParam(getFunction(api.functions, "GuiSpinner"), 2); + setOutParam(getFunction(api.functions, "GuiValueBox"), 2); + setOutParam(getFunction(api.functions, "GuiListView"), 2); ignore("GuiListViewEx"); + ignore("GuiTextBox"); ignore("GuiTextInputBox"); - ignore("GuiGetIcons"); + //setOutParam(getFunction(api.functions, "GuiTextInputBox")!, 6) ignore("GuiTabBar"); + ignore("GuiGetIcons"); ignore("GuiLoadIcons"); // TODO: Parse and support light struct ignore("CreateLight"); diff --git a/src/bindings/js_raylib_core.h b/src/bindings/js_raylib_core.h index 3ab25f0..08386d4 100644 --- a/src/bindings/js_raylib_core.h +++ b/src/bindings/js_raylib_core.h @@ -8907,16 +8907,55 @@ static JSValue js_guiComboBox(JSContext * ctx, JSValueConst this_val, int argc, return ret; } -static JSValue js_guiTextBox(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { +static JSValue js_guiDropdownBox(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { Rectangle* bounds_ptr = (Rectangle*)JS_GetOpaque2(ctx, argv[0], js_Rectangle_class_id); if(bounds_ptr == NULL) return JS_EXCEPTION; Rectangle bounds = *bounds_ptr; - char * text = (char *)JS_ToCString(ctx, argv[1]); - int textSize; - JS_ToInt32(ctx, &textSize, argv[2]); + const char * text = (const char *)JS_ToCString(ctx, argv[1]); + int active_out; + int * active = &active_out; bool editMode = JS_ToBool(ctx, argv[3]); - bool returnVal = GuiTextBox(bounds, text, textSize, editMode); + bool returnVal = GuiDropdownBox(bounds, text, active, editMode); JS_FreeCString(ctx, text); + JS_SetPropertyStr(ctx, argv[2], "active", JS_NewInt32(ctx,active_out)); + JSValue ret = JS_NewBool(ctx, returnVal); + return ret; +} + +static JSValue js_guiSpinner(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Rectangle* bounds_ptr = (Rectangle*)JS_GetOpaque2(ctx, argv[0], js_Rectangle_class_id); + if(bounds_ptr == NULL) return JS_EXCEPTION; + Rectangle bounds = *bounds_ptr; + const char * text = (const char *)JS_ToCString(ctx, argv[1]); + int value_out; + int * value = &value_out; + int minValue; + JS_ToInt32(ctx, &minValue, argv[3]); + int maxValue; + JS_ToInt32(ctx, &maxValue, argv[4]); + bool editMode = JS_ToBool(ctx, argv[5]); + bool returnVal = GuiSpinner(bounds, text, value, minValue, maxValue, editMode); + JS_FreeCString(ctx, text); + JS_SetPropertyStr(ctx, argv[2], "value", JS_NewInt32(ctx,value_out)); + JSValue ret = JS_NewBool(ctx, returnVal); + return ret; +} + +static JSValue js_guiValueBox(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Rectangle* bounds_ptr = (Rectangle*)JS_GetOpaque2(ctx, argv[0], js_Rectangle_class_id); + if(bounds_ptr == NULL) return JS_EXCEPTION; + Rectangle bounds = *bounds_ptr; + const char * text = (const char *)JS_ToCString(ctx, argv[1]); + int value_out; + int * value = &value_out; + int minValue; + JS_ToInt32(ctx, &minValue, argv[3]); + int maxValue; + JS_ToInt32(ctx, &maxValue, argv[4]); + bool editMode = JS_ToBool(ctx, argv[5]); + bool returnVal = GuiValueBox(bounds, text, value, minValue, maxValue, editMode); + JS_FreeCString(ctx, text); + JS_SetPropertyStr(ctx, argv[2], "value", JS_NewInt32(ctx,value_out)); JSValue ret = JS_NewBool(ctx, returnVal); return ret; } @@ -9026,6 +9065,22 @@ static JSValue js_guiGrid(JSContext * ctx, JSValueConst this_val, int argc, JSVa return ret; } +static JSValue js_guiListView(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { + Rectangle* bounds_ptr = (Rectangle*)JS_GetOpaque2(ctx, argv[0], js_Rectangle_class_id); + if(bounds_ptr == NULL) return JS_EXCEPTION; + Rectangle bounds = *bounds_ptr; + const char * text = (const char *)JS_ToCString(ctx, argv[1]); + int scrollIndex_out; + int * scrollIndex = &scrollIndex_out; + int active; + JS_ToInt32(ctx, &active, argv[3]); + int returnVal = GuiListView(bounds, text, scrollIndex, active); + JS_FreeCString(ctx, text); + JS_SetPropertyStr(ctx, argv[2], "scrollIndex", JS_NewInt32(ctx,scrollIndex_out)); + JSValue ret = JS_NewInt32(ctx, returnVal); + return ret; +} + static JSValue js_guiMessageBox(JSContext * ctx, JSValueConst this_val, int argc, JSValueConst * argv) { Rectangle* bounds_ptr = (Rectangle*)JS_GetOpaque2(ctx, argv[0], js_Rectangle_class_id); if(bounds_ptr == NULL) return JS_EXCEPTION; @@ -10153,13 +10208,16 @@ static const JSCFunctionListEntry js_raylib_core_funcs[] = { JS_CFUNC_DEF("guiToggleGroup",3,js_guiToggleGroup), JS_CFUNC_DEF("guiCheckBox",3,js_guiCheckBox), JS_CFUNC_DEF("guiComboBox",3,js_guiComboBox), - JS_CFUNC_DEF("guiTextBox",4,js_guiTextBox), + JS_CFUNC_DEF("guiDropdownBox",4,js_guiDropdownBox), + JS_CFUNC_DEF("guiSpinner",6,js_guiSpinner), + JS_CFUNC_DEF("guiValueBox",6,js_guiValueBox), JS_CFUNC_DEF("guiSlider",6,js_guiSlider), JS_CFUNC_DEF("guiSliderBar",6,js_guiSliderBar), JS_CFUNC_DEF("guiProgressBar",6,js_guiProgressBar), JS_CFUNC_DEF("guiStatusBar",2,js_guiStatusBar), JS_CFUNC_DEF("guiDummyRec",2,js_guiDummyRec), JS_CFUNC_DEF("guiGrid",4,js_guiGrid), + JS_CFUNC_DEF("guiListView",4,js_guiListView), JS_CFUNC_DEF("guiMessageBox",4,js_guiMessageBox), JS_CFUNC_DEF("guiColorPicker",3,js_guiColorPicker), JS_CFUNC_DEF("guiColorPanel",3,js_guiColorPanel),