What's going on with the demo above? Click and interact with some example UI elements. Click, drag and resize the blue panel. Can't run the demo above? Let me know in the comments, please.


Welcome to gooey, a simple UI library for Gamemaker LTS+

Latest version is 2024.1.


NEW: 2024.1 version released which fixes a bug with scrolling children (thanks to @luttje and @gellyware for detecting this!)

DISCLAIMER: gooey is provided as is. Few/no new features will probably ever be added, and updates will only be provided if there are serious, game-breaking bugs and I have enough time and energy to deal with them.

Download the latest version

Access the API documentation (online)
(Download offline HTML documentation below) 

Access the BitBucket repository



What exactly is gooey?

gooey is a small UI library that lets you create menus, HUD and other UI elements in Gamemaker, with a few lines of code, using sprites.

gooey currently supports the following widgets:

  • Panels (the containers for everything else)
  • Groups  (to group stuff together)
  • Grids (to layout stuff in Panels or Groups)
  • Buttons
  • Canvases (surfaces)
  • Checkboxes
  • Dropdowns
  • Option Groups
  • Progress Bars
  • Sliders
  • Sprites
  • Tabs (tabbed Panels) 
  • Text Boxes
  • Text (labels)
  • Spinners [NEW FOR 2023.8]


gooey has the following features:

  • Drag and drop setup - see below
  • Intuitive code for setting up the UI - focus on the What, not on the How
  • Struct based, with a set it and forget it manager object
  • Setters/getters for properties
  • Fluid interface (i.e. you can chain methods together)
  • Kenney's amazing UI asset pack sprites are included for testing
  • Text based on JujuAdams's superb Scribble renderer

Who is gooey for?

gooey is suitable for developers making prototypes, jam games or small projects. gooey is probably not suitable for big scale production or commercial games.

Who made gooey?

gooey was made by manta ray, an intermediate Gamemaker hobby developer from Mexico.

How can I use gooey in my project?

  1. Download the .YYMPS file and import it as a local package in your project.
  2. Drag the UI manager object into your first room.  Make sure it's created before other objects that create/interact with UI, by using Instance Creation Order.
  3. Use gooey's structs and methods to create and set up your widgets.
    1. Note you will need at least one sprite. gooey is 100% sprite-based. gooey includes Kenney's UI asset pack.

For example, to create a simple draggable/resizable panel, type this line of code in an object's Create event or similar place (make sure it only triggers once):

var _panel = new UIPanel("Example", 20, 40, 400, 300, blue_panel);

This will create the panel at (20,40) from the top-left anchor point of the GUI layer (be sure to read about available anchor points for more info), of size 400x300, using sprite `blue_panel` (again, from Kenney's included UI pack), and name it Example.

I will also post a video in the coming days demonstrating actual use of gooey on a game project.

What are some caveats to understand before using gooey (spoiler: a lot)?

  1. gooey is currently in alpha/preview version. Bugs will inevitably pop up. Use the comments section below or contact me directly in Discord or email if you find one.
  2. gooey is built for Gamemaker LTS or greater. If you don't have this version, you should probably look elsewhere.
  3. gooey is based on JujuAdams's Scribble for rendering text. 
    1. Scribble is actually included in the local package file. The file will be periodically updated as new versions of Scribble come out.
    2. if you want to use a different version of Scribble for whatever reason in your project, you can remove the built-in one and use whatever version you need. If you do that, it is recommended to set the following macro variables in Scribble configuration scripts:
      • #macro SCRIBBLE_ALLOW_TEXT_GETTER true
        (in __scribble_config_behaviours)
      • #macro SCRIBBLE_DEFAULT_HALIGN fa_center <br> #macro SCRIBBLE_DEFAULT_VALIGN fa_middle
        (in __scribble_config_defaults)
  4. gooey is not fully optimized and will probably never be.
  5. gooey's documentation is currently very basic, I plan on expanding it on the future.
  6. gooey's setters and getters do not currently have validations on input, so exception handling is harder right now.
  7. gooey is designed for, and currently aimed at, mouse or touch only games, so it doesn't currently handle controller or keyboard navigation natively at the moment, and probably never will.
  8. gooey uses the GUI layer to draw GUI, so it's mainly for non-diegetic UI.
  9. gooey is 100% sprite based. You cannot draw UI components with primitives or basic shapes using gooey.
  10. There's probably more...

What export platforms are supported?

Theoretically all of them. However, gooey has only been unit-tested on Windows, Opera GX, HTML5 and Android at the moment.

Does gooey support live reloading/editing?

Not natively. However, I highly recommend pairing it with YellowAfterlife's mind-blowing GMLive extension.

Where can I find the documentation for gooey?

The latest documentation on every gooey widget and function can be found here.

Why doesn't gooey have feature ___?

I made gooey for my own use, with the scope intended, and decided to share it with the community. I do have a backlog, but honestly at this point I'd rather spend my time using it for creating games than actually developing it further.

Also, as the famous mantra says:

Done is better than perfect

Why isn't gooey super polished?

See the mantra in the question above.

Why is the code not consistent/a bit ugly/etc.?

See mantra above (heh). Jokes aside, this is the third iteration already, and I had to change stuff on the fly and didn't think about all the implications of certain features from the beginning. Hence, some of the code is not abstracted correctly / is redundant / etc.

Can I fork it / improve it?

Be my guest. I have a BitBucket repo if you want to check it out.

Why is this called gooey?

GUI is an homophone with gooey. Compare here and here.

What license does gooey have?

MIT (https://choosealicense.com/licenses/mit/). Do whatever you please with it, just don't blame me if things go awfully wrong :) 

Note that Scribble's license is also MIT and and Kenney's UI asset pack is CC0.



Changelog

2024.1Fixed a bug where children widgets would not scroll in many cases, when scrolling the parent
2023.11Optimizes the way callbacks are handled, which in turn circumvents a Gamemaker bug which prevented directly assigning function names to callbacks
2023.10Fixes a problem with UIProgressBar widgets set to repeating behavior and adds "click to set" option (instead of "click to modify value") to UISlider widgets
2023.8.1Fixed (hopefully all) invalid reference type errors when using gooey with Gamemaker 2023.8.145 runtime (or later).
2023.8Greatly improved slider feel and usage, added a UISpinner widget, added support for Scribble typists within UIText elements and fixed a few bugs.
0.1 alphagooey Preview version.
StatusReleased
CategoryTool
PlatformsWindows, macOS, Linux, Android, HTML5
Rating
Rated 5.0 out of 5 stars
(2 total ratings)
Authormanta ray
Made withGameMaker
TagsGameMaker, Graphical User Interface (GUI)

Download

Download
gooey 2024.1 - Scribble 8.7.yymps 996 kB
Download
gooey 2024.1 documentation.zip 90 kB
Download
gooey 2023.11 - Scribble 8.7.yymps 996 kB
Download
gooey 2023.10 - Scribble 8.7.yymps 996 kB
Download
gooey 2023.8.1 - Scribble 8.7.yymps 996 kB
Download
gooey 2023.8 - Scribble 8.7.yymps 995 kB

Comments

Log in with itch.io to leave a comment.

Hi, the UITextBox doesn't work for me. The caret is always shown in the top left corner of the sprite, also the text . I'm using the latest GM and gooey version 2024.1. I was able to position place holder text (which should be "Hello world" in the screenshot) with fa_left to make it readable, the caret still appears at the top left corner and the input behaves like it's fa_center'ed.

Am I'm doing something wrong? I tried to find a solution in the docs, but wasn't successfull.

(1 edit)

Hi and thanks for using the library! Hope it's useful.

You can use the .setTextFormat() method to fix it:


var _textbox = new UITextBox("Test_Textbox", 80, 30, 150, 50, grey_button08); 
_textbox.setTextFormat("[fa_left][fa_top][c_black]").setPlaceholderText("Hello World"); 
_panel.add(_textbox);

Full code for example above:

var _panel = new UIPanel("Test", 40, 40, 300, 400, yellow_panel); 
_panel.setTitleFormat("[fa_center][fa_top][c_black]").setTitle("NEW GAME"); 
var _label = new  UIText("Test_SeedText", 30, 30, "[fa_left][fa_top][c_black]Seed: ");
_panel.add(_label);
var _textbox = new UITextBox("Test_Textbox", 80, 30, 150, 50, grey_button08);
_textbox.setTextFormat("[fa_left][fa_top][c_black]").setPlaceholderText("Hello World");
_panel.add(_textbox);
var _button = new UIButton("Test_Button", 0, -20, 280, 40, "Start game", yellow_button00, UI_RELATIVE_TO.BOTTOM_CENTER);
_button.setCallback(UI_EVENT.LEFT_RELEASE, function() {     show_message(UI.get("Test_Textbox").getText()); });
_panel.add(_button);


Let me know if there's anything else I can help with!

Ah, I forgot fa_top! Thanks a lot! Now it works fine. 

It's a wonderful library, thanks for making it. :) 

Are there downloadable docs or just online?

Hey, I apologize for the delay. Currently, documentation in HTML format is only available online. However, HTML documentation is generated automatically from the JSDoc comments on the Gamemaker script itself (above each function/method), using another tool I wrote named GMLDocMaker. So, this means that all documentation is essentially inside the gooey script. You can use the find functionality (or middle click a function name in Gamemaker) and it will show you the explanation.

If you still need the offline documentation I could try to generate an offline version in a format like PDF or so. It won't be pretty, but it might work.

Let me know if I can further help!

Thanks for the reply manta ray! I will look into the GMLDocMaker that you wrote. 

Is it possible to make the items in the grid scrollable from within the panel? I.e. level select buttons? Seems like the buttons I'm making are getting compressed to the panel size.

(1 edit)

Hi, hope you're fine!

I have published a 2024.1 version which (hopefully) fixes the scroll bug.  I also updated the online documentation and generated an offline version of the HTML documentations (you can download it as well).

With this version, using a somewhat "creative" approach, you'll be able to create a scrollable grid like so:


Test code is as follows:

if (!UI.exists("LevelSelectPanel")) {     var _num_levels = 30;     var _columns = 3;          var _panel = new UIPanel("LevelSelectPanel", 500, 200, 500, 500, blue_panel, UI_RELATIVE_TO.MIDDLE_CENTER);     _panel.setTitle("Level Select").setTitleAnchor(UI_RELATIVE_TO.MIDDLE_CENTER);          var _container = new UIGrid("ContainerGrid", 2, 1);     _container.setRowProportions([0.2, 0.8]);     _container.setShowGridOverlay(true);     _container.getCell(1,0).setClipsContent(true);          var _cnt = _container.addToCell(new UIGroup("GridContainer", 0, 0, 500, 1500, red_panel), 1, 0);          var _grid = new UIGrid("LevelsGrid", _num_levels div _columns, _columns);     _grid.setSpacingHorizontal(20).setSpacingVertical(20).setMarginLeft(50).setMarginRight(50).setMarginTop(10).setMarginBottom(10);               for (var _level = 0; _level < _num_levels; _level++) {         var _button = new UIButton("Level"+string(_level+1), 0, 0, 0, 0 , "Level "+string(_level+1), blue_button00, UI_RELATIVE_TO.MIDDLE_CENTER);         _button.setInheritWidth(true).setInheritHeight(true).setCallback(UI_EVENT.LEFT_RELEASE, method({level: _level}, function() {             show_message("You selected level "+string(level+1));         }));         _grid.addToCell(_button, _level div _columns, _level % _columns);     }     _container.addToCell(_cnt, 1, 0);     _cnt.add(_grid);     _panel.add(_container); }  if (keyboard_check(vk_down))    UI.get("ContainerGrid").getCell(1,0).scroll(UI_ORIENTATION.VERTICAL, -1, 5); if (keyboard_check(vk_up))        UI.get("ContainerGrid").getCell(1,0).scroll(UI_ORIENTATION.VERTICAL, 1, 5);

Hope you find it useful!


Thank you for the quick reply, fix, and demo! I'm having a hard time trying it out because the buttons get scaled strangely (I've had this happen a few times now when dragging the panel)

Can you send me your project or object code?

Sharing another snippet of something I had to kinda of workaround.

I have a couple UIPanel widgets that I add as children to a grid. Upon closing the room I call a function to remove all widgets that are 'bound' to the room. Sadly panels that are parented to a Grid don't fully delete themselves. I had to use this workaround to get them to completely delete:

if (_widget.getType() == UI_TYPE.PANEL) {
    _widget.__parent.remove(_widget.getID());
    UI.__destroy_widget(_widget);
}
_widget.destroy()


Full snippet for the logic to register and later remove widgets when the room ends:

global.widgets_to_remove = []
// Registers a widget that will be removed when the room ends
function widget_bound_to_room(_widget)
{
    array_push(global.widgets_to_remove, _widget)    
    return _widget
}
// Called on room end to remove bound widgets
function widgets_remove_room_bound()
{
    for (var _i = array_length(global.widgets_to_remove) - 1; _i >= 0; _i--) {
        var _widget = array_pop(global.widgets_to_remove)
        
        custom_event_call("widget_removing_room_bound", {
            widget_to_remove: _widget    
        });
        
        // Workaround for panels not removing correctly
        if (_widget.getType() == UI_TYPE.PANEL) {
            _widget.__parent.remove(_widget.getID());
            UI.__destroy_widget(_widget);
        }
        
        _widget.destroy()
    }
}


To use it:

1. When creating widgets call widget_bound_to_room on it, e.g:

var _crafting_group = widget_bound_to_room(new UIGroup("CraftingGroup", 0, 0, 250, 0, spr_back, UI_RELATIVE_TO.MIDDLE_CENTER))
_crafting_group.setInheritHeight(true)
_crafting_group.add(widget_bound_to_room(new UIPanel("ParentedPanel", _x, _y, _width, _height, spr_frame_simple)))


2. When the room ends call:

widgets_remove_room_bound();
(1 edit)

Hi again, 

UIPanels are not meant to be nested (at least I didn't design it that way!), so I'm interested in your use case for that and how are you implementing it. I can see you would have problems destroying them, but there might be other things that don't work as you think, just because again, when I thought about UIPanels I always conceived them as top-level widgets.

It would be really useful if you could share your code and/or images to see how you are using them nested. 

Best,

manta ray

(2 edits)

Heya again,

Thanks for explaining that the UIPanels weren't meant for that. I used them to create panels with a background sprite, but now that you said that I was doing something unintended- I double-checked the docs and found that a UIGroup can have a background sprite as well.

I guess the term 'panel' gave me the illusion that they would be similar to WinForms Panels, but they're more akin to Forms there I guess. I've switched to UIGroups for my purposes, but will leave the code snippet up since it may be useful to someone else for other reasons.

Thanks again for your quick support!

Thanks so much for sharing this awesome UI framework for free. Saves me a ton of work and it functions intuitively and is quite stable.

One thing I want to share is that it seems UIGrid.scroll doesn't move the children in cells. The cell groups themselves move (I can see that with setShowGridOverlay), but the children stay where they are initially placed.


For others struggling with this: I've made this (temporary) workaround that hotfixes the problem. Call it on the grid you want to scroll with the same parameters you would give to the .scroll method:

function ui_grid_scroll(_ui_grid, _orientation = UI_ORIENTATION.HORIZONTAL, _sign = 1, _amount = UI_SCROLL_SPEED)
{
    // Just this scrolls the cell groups, but their children aren't updated. BUG?
    _ui_grid.scroll(_orientation, _sign, _amount)
    
    // HOTFIX that triggers the children to update to their correct position
    var _children = _ui_grid.getChildren()
    
    for (var _i = 0; _i < array_length(_children); _i++) {
        var _child = _children[_i]
        
        _child.scroll(_orientation, _sign, 0)
    }    
}

Thanks so much for the compliments, I'm glad to hear it's being useful!

I will check this and fix it if necessary, as soon as I can. One question, are your child elements in the grid cells UIPanels (as commented in the other question above)?

Heya :)

No the UIGrid did not contain any UIPanels, however the structure was somewhat complex:

  • UIPanel
    • UIGrid (2 rows, 1 column)
      • UIGrid in 1st row (1 row, 3 columns - column definitions [1, 1, 1])
        • UIButton (column 1)
        • UIButton (column 2)
        • UIButton (column 3)

Sadly I can't share a minimal, reproducible example atm since a lot of the code is intertwined with custom functions and little time to extract it.

(+1)

Hi, just to let you know, I just published 2024.1 version which hopefully fixes the scrolling bug.

Hey, it looks like the documentation site is down. Any chance you could get it back up? Thank you!

Hi @arronmarc, apologies for this problem. It should be fixed by now, it had an invalid redirect rule that was causing the problem. Thank you for your patience!

Amazing, thank you so much!

hi, was just trying to find a simple method for returning co-ords for anything a panel, widget etc. Found my way through browsing functions to work out that __dimensions works fine, just checking is the docs suppose to say __dimensions instead of __UIDimensions?

There's a getDimensions() method for all widgets (since they all inherit from the UIWidget struct). Accessing with __dimensions is also ok but not preferred (you should access with the given getters/setters).

question: So I'm creating an upgrade button and i want a button sprite but also the sprite of the object ontop.

I did something like this:
vehicle_upgrade_image = vehicle_upgrade.add(new UISprite("VehicleUpgradeImage", 0, -8, _sprite, , , , UI_RELATIVE_TO.MIDDLE_CENTER));

works great.. However i cant click on the button while mousing over the sprite.

What's the most optimal way of doing this, I may investigate the UIGroup widget but just wondering if there is a hidden method that is like click-through but for other ui components

(1 edit)

Hi, there's currently no click-through method for elements. What I'm doing to circumvent this in my games is I'm setting the LEFT_RELEASE callback of the sprite and calling the button callback:


with (_panel.add(new UISprite("YourSprite", _x, _y, _w, _h, _spr, 0))) {
    setCallback(UI_EVENT.LEFT_RELEASE, function() {
        var _f = UI.get("YourButton").getCallback(UI_EVENT.LEFT_RELEASE);
        _f();
    });
}

yeah this is what i ended up doing https://ldjam.com/events/ludum-dare/53/clickexpress you can see some ui stuff in action here

Cool! Haven't been able to check it out (I'm at work) but I followed you on the site so I remember later. I also made an LD game for this edition and used gooey:


https://ldjam.com/events/ludum-dare/53/medieval-messenger

also sorry for spamming ur itch comments HAHAH, much love for the support this library is awesome

No problem! Happy gamemaking

so turns out i was dumb and i figured it out on stream!

-- Question? Rows should go horizontally no? This is how i know them from spreadsheets.. However idk if its a bug or if you set it up this way. Rows go vertically and collums at the moment go horizontally.

If you could clarify if this is intentional that would be great :)

slowly making progress figuring it out thanks for the hard work and speedy replies <3

that being said it's not really a big issue i can invert it.

Followed your example code and i am getting there!! Thank you for this library it is awesome


Great that you managed to get it working on your own!

The same as with all spreadsheets (and most programming languages), rows indeed "go vertically" (this means they increment as you go down the grid) and columns "go horizontally" (this means they increment as you go from left to right). So if you have a 4x3 grid, it looks like this:


row=0, col=0row=0, col=1row=0, col=2
row=1, col=0row=1, col=1row=1, col=2
row=2, col=0row=2, col=1row=2, col=2
row=3, col=0row=3, col=1row=3, col=2


So if you need to add a widget to the bottom left cell, you'd say

_grid.addToCell(_widget, 3, 0);

This is the same as it happens on spreadsheets (the only difference being that rows and columns on spreadsheets start with 1 not with 0).


Also share your stream video if you can!


Best,

José

https://www.twitch.tv/videos/1790632218 here is the stream VOD was fun to bring it together based on your example code you wrote. Obviously you can see i have some issues still, my GUi rescaling makes things a bit tricky with text, I'm still having a few issues around nice ways of updating the UI when picking up items. Is there a way to not have to redraw the UI when updating for example my ds_grid?

Hi, not sure if I understand correctly. The way to update a widget once it's created is to either set a binding to a variable or method (if simple, i.e. the value of a UIProgressbar or the text of a UIText) or use setPreRenderCallback/setPostRenderCallback. Once you set it up, it's automatic.

When you say "when picking up things" I'm imagining you are not pausing the game while the inventory is displayed, and you want the inventory to update when picking up a new item. In this case, you can set the redraw call when picking up the object. Make a parent object with that logic and you can inherit all your items from that object, so all of them trigger the redraw.

Let me know if I understood correctly or not, thanks. By the way, nice graphics in your project!

yeah,i was asking if there is a way to avoid having to trigger a redraw, I did get it working how I want it but I'm just wondering if recalling the "show_inventory" function is going to cause performance issues in the long run. If it's not really going to cause to many issues or be non-performant that's all good

___________________________________________

############################################################################################

ERROR in

action number 1

of Draw Event

for object object_ui:

Unable to find instance for object index -4

 at gml_Script_anon_UIGrid_gml_GlobalScript_scr_gooey_Widgets_225168_UIGrid_gml_GlobalScript_scr_gooey_Widgets (line 4398) -                                                  _widget.setDimensions(_x, _y, _w, _h);

############################################################################################

gml_Script_anon_UIGrid_gml_GlobalScript_scr_gooey_Widgets_225168_UIGrid_gml_GlobalScript_scr_gooey_Widgets (line 4398)

gml_Script_anon___UIWidget_gml_GlobalScript_scr_gooey_Widgets_278126___UIWidget_gml_GlobalScript_scr_gooey_Widgets (line 5319) -                                           _id.__updateGridDimensions();

gml_Script_anon_gml_Object_object_ui_Create_0_124_gml_Object_object_ui_Create_0 (line 11) -        var _grid_meta = _panel.add(new UIGrid("MetaGrid", 2, 1));

gml_Object_object_ui_Draw_0 (line 54) -               self.draw_inventory();       

___________________________________________

I randomly get this bug around grid usage by the way. I'm sure it's because I haven't implimented them correctly but thought you should know.


// Create grid with two rows, one column and 80%-20% proportions (upper part will be the inventory and lower part the description)

var _grid_meta = _panel.add(new UIGrid("MetaGrid", 2, 1));

_grid_meta.setRowProportions([0.8,0.2]).setMarginTop(_panel.getDragBarHeight());


-- This is what I wrote around grids

I think this is due to the grid width not being the same as the height!

When it's the same there is no issues hmmnn

Kindly share your full code in pastebin or similar (not pasted here in the comments) or share your GM project, so I can help debug it.

One possible cause that will bring a similar error with UIGrids is you're trying to add a widget to a non-existent grid cell. The addToCell(_row, _col) method needs the row specified first and then the column.

it would be really awesome if there was more info or tutorials on using the library. Do you plan to make a few more videos?

For example I'm thinking how to use it to build a inventory, I'm playing around with the UIGrid but yeah I think if there was a tutorial that went over how widgets link up more or what they are used for will help.

I appriciate the docs and the current video that is up.

Hi @NataeGames, 

I created a tiny demo game with a basic inventory for you!

You can play with it at https://biyectivo.com/Inventory/

You can also download the full source code at https://bitbucket.org/biyectivo/inventory/src/main/. I tried to comment the key parts so it's faster for you to understand it.

Hope you like it!

wow I really appriciate that! I'll have a browse and get a grasp of how it was made thank you!

oh side note https://biyectivo.com/gooey/doc_scr_UI_Widgets.gml.html#UIButton the docs are bugged you cant view the whole page as the sidebar cuts it off :c

Thanks for the heads up, can you share your browser, browser version and OS please?

Windows 11 -- operaGX.
I zoomed out to 80% and i can see it thankfully

YO, amazing work. I praise this so hard, im going to implement it into my game as

Odd Nite(wurmhole) said, I'm just getting into UI for my project and i was just browsing for any libraries to reference but this has allot of great infostructure.

I appreciate the hard work with this library thank you so much for your efforts 

Great, hope you find it useful!

You are a Saint. This comes right as I’ve started work on the UI of my project! 

hopefully it's useful. Let me know :)