raysan5 / raygui

A simple and easy-to-use immediate-mode gui library
zlib License
3.52k stars 300 forks source link

Unable to use the GUI if it's rendered on top of 3D viewport in Raylib #423

Closed alexlnkp closed 2 months ago

alexlnkp commented 2 months ago

For some unknown reason the GUI just doesn't respond to any user input, be it a button, checkbox or anything really, although the GUI IS rendered properly and it also reacts to its state changing via GuiDisable(); or GuiEnable(); (the GUI fades in & out). I really do believe that I was just being careless while defining the GUI, but I literally have no idea what to even look for in order to fix the issue. Raylib version I'm using is 5.0, RayGui I'm using is the latest from the master branch, only with a patch applied to fix a simple issue with it (without patching it wont compile); Just in case, here's the patch:

--- a/raygui.h  2024-08-06 06:18:52.461047534 +0700
+++ b/raygui.h  2024-08-06 06:18:15.813622145 +0700
@@ -2971,6 +2971,37 @@
     return result;
 }

+// Get float value from text
+// NOTE: This function replaces atof() [stdlib.h]
+// WARNING: Only '.' character is understood as decimal point
+static float TextToFloat(const char *text)
+{
+    float value = 0.0f;
+    float sign = 1.0f;
+
+    if ((text[0] == '+') || (text[0] == '-'))
+    {
+        if (text[0] == '-') sign = -1.0f;
+        text++;
+    }
+
+    int i = 0;
+    for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+
+    if (text[i++] != '.') value *= sign;
+    else
+    {
+        float divisor = 10.0f;
+        for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+        {
+            value += ((float)(text[i] - '0'))/divisor;
+            divisor = divisor*10.0f;
+        }
+    }
+
+    return value;
+}
+
 // Floating point Value Box control, updates input val_str with numbers
 // NOTE: Requires static variables: frameCounter
 int GuiValueBoxFloat(Rectangle bounds, const char *text, char *textValue, float *value, bool editMode)
@@ -5605,37 +5636,6 @@
     return value*sign;
 }

-// Get float value from text
-// NOTE: This function replaces atof() [stdlib.h]
-// WARNING: Only '.' character is understood as decimal point
-static float TextToFloat(const char *text)
-{
-    float value = 0.0f;
-    float sign = 1.0f;
-
-    if ((text[0] == '+') || (text[0] == '-'))
-    {
-        if (text[0] == '-') sign = -1.0f;
-        text++;
-    }
-
-    int i = 0;
-    for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
-
-    if (text[i++] != '.') value *= sign;
-    else
-    {
-        float divisor = 10.0f;
-        for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
-        {
-            value += ((float)(text[i] - '0'))/divisor;
-            divisor = divisor*10.0f;
-        }
-    }
-
-    return value;
-}
-
 // Encode codepoint into UTF-8 text (char array size returned as parameter)
 static const char *CodepointToUTF8(int codepoint, int *byteSize)
 {

With it applied, everything compiles and works, except for RayGui. It does render without issues, everything seems to be in order, but buttons, checkboxes and literally all of the widgets can not be used at all by the user. Hovering over them doesn't even reveal the outline of them, which is a good indication that it simply doesn't even register the fact that mouse cursor is over them.

I assumed specific RayGui versions are only allowed to be used by specific Raylib versions, but if that were the case, I'd hope to find a compatibility list of sorts, that allows to quickly pinpoint what version of RayGui to use on what Raylib version. Of course I could simply pull from master of both Raylib and Raygui, but I personally prefer stable releases over simply the latest commits, and due to RayGui's latest tag being ~4.0, while Raylib's latest tag is 5.0, it is generally really hard to find out which version of RayGui is compatible with a specific version of Raylib. Although, I did try to use a version of RayGui released 2 days after the Raylib's 5.0 release was made, still the same issue persisted, GUI was drawn, wasn't interactible by the user.

Here's a snippet of my code:

void DrawGUI(void) {
    if (GuiButton((Rectangle){.height=30.0f, .width=100.0f, .x=0.0f, .y=0.0f}, "#80#Cube")) {
        CreateCube();
    }
}

void Draw(void) {
    BeginDrawing(); {
        ClearBackground(RAYWHITE);

        BeginMode3D(*cam); {
            DrawCubes();

            DrawGrid((int)GROUND_TOTAL_LENGTH, 1.0f);

        } EndMode3D();
        DrawGUI();
    } EndDrawing();
}

int main(void) {
    SetConfigFlags(FLAG_WINDOW_UNDECORATED | FLAG_MSAA_4X_HINT);
    InitWindow(1280, 720, "insert_a_title_here");
    SetTargetFPS(60);

    InitGlobal();

    while (!WindowShouldClose()) {
        HandleEvents();

        Update();

        Draw();
    }

    CloseWindow();

    return 0;
}
Sebbito commented 2 months ago

I have a similar issue. For me buttons do work (apply and cancel button in example below) but drop down boxes and check boxes don't. I also tried with locking and unlocking at different times but it still doesn't work :T

My code:

// I have a global `Settings` struct

const char* fps_str = "30;60;144";
const std::vector<int> fps = {30, 60, 144};

const char* res_str = "720x480;1280x720;1920x1080;2560x1440;3840x2160";
const std::vector<Vector2> resolutions = {
  {720, 480},
  {1280, 720},
  {1920, 1080},
  {2560, 1440},
  {3840, 2160},
};

//...

int open_settings() {
  bool ResolutionPickerEditMode = false;
  int ResolutionPickerActive = 1;
  bool RefreshRatePickerEditMode = false;
  int RefreshRatePickerActive = 1;
  bool FullScreenCheckboxChecked = Settings.full_screen; // `Settings` is defined elsewhere
  bool CancelButtonPressed = false;
  bool ApplyButtonPressed = false;

  Vector2 anchor01 = { 392, 160 };

  if (ResolutionPickerEditMode || RefreshRatePickerEditMode)
    GuiLock();

  GuiGroupBox((Rectangle){ anchor01.x + 0, anchor01.y + 8, 432, 216 }, "Settings");
  GuiLabel((Rectangle){ anchor01.x + 24, anchor01.y + 48, 120, 24 }, "Resolution");
  GuiLine((Rectangle){ anchor01.x + 24, anchor01.y + 24, 384, 16 }, "Video");
  GuiLine((Rectangle){ anchor01.x + 24, anchor01.y + 160, 384, 16 }, NULL);
  GuiLabel((Rectangle){ anchor01.x + 24, anchor01.y + 112, 120, 24 }, "Refresh Rate");
  GuiLabel((Rectangle){ anchor01.x + 24, anchor01.y + 80, 120, 24 }, "Full Screen");
  GuiCheckBox((Rectangle){ anchor01.x + 264, anchor01.y + 80, 24, 24 }, NULL, &FullScreenCheckboxChecked);
  CancelButtonPressed = GuiButton((Rectangle){ 704, 344, 72, 24 }, "Cancel"); 
  ApplyButtonPressed = GuiButton((Rectangle){ 608, 344, 72, 24 }, "Apply"); 

  GuiUnlock();

  if (GuiDropdownBox((Rectangle){ anchor01.x + 264, anchor01.y + 112, 120, 24 }, fps_str, &RefreshRatePickerActive, RefreshRatePickerEditMode))
    RefreshRatePickerEditMode = !RefreshRatePickerEditMode;

  if (GuiDropdownBox((Rectangle){ anchor01.x + 264, anchor01.y + 48, 120, 24 }, res_str, &ResolutionPickerActive, ResolutionPickerEditMode))
    ResolutionPickerEditMode = !ResolutionPickerEditMode;

  if (ApplyButtonPressed) {
    Settings.resolution = resolutions[ResolutionPickerActive];
    Settings.fps = fps[RefreshRatePickerActive];
    Settings.full_screen = FullScreenCheckboxChecked;
    update_window_settings();
  }

  return (CancelButtonPressed || ApplyButtonPressed);
}

// ...

Full code from here: https://codeberg.org/Sebito/collatz/src/branch/feat/settings File: src/settings.cpp

alexlnkp commented 2 months ago

I have a similar issue. For me buttons do work (apply and cancel button in example below) but drop down boxes and check boxes don't. I also tried with locking and unlocking at different times but it still doesn't work :T

My code:

// I have a global `Settings` struct

const char* fps_str = "30;60;144";
const std::vector<int> fps = {30, 60, 144};

const char* res_str = "720x480;1280x720;1920x1080;2560x1440;3840x2160";
const std::vector<Vector2> resolutions = {
  {720, 480},
  {1280, 720},
  {1920, 1080},
  {2560, 1440},
  {3840, 2160},
};

//...

int open_settings() {
  bool ResolutionPickerEditMode = false;
  int ResolutionPickerActive = 1;
  bool RefreshRatePickerEditMode = false;
  int RefreshRatePickerActive = 1;
  bool FullScreenCheckboxChecked = Settings.full_screen; // `Settings` is defined elsewhere
  bool CancelButtonPressed = false;
  bool ApplyButtonPressed = false;

  Vector2 anchor01 = { 392, 160 };

  if (ResolutionPickerEditMode || RefreshRatePickerEditMode)
    GuiLock();

  GuiGroupBox((Rectangle){ anchor01.x + 0, anchor01.y + 8, 432, 216 }, "Settings");
  GuiLabel((Rectangle){ anchor01.x + 24, anchor01.y + 48, 120, 24 }, "Resolution");
  GuiLine((Rectangle){ anchor01.x + 24, anchor01.y + 24, 384, 16 }, "Video");
  GuiLine((Rectangle){ anchor01.x + 24, anchor01.y + 160, 384, 16 }, NULL);
  GuiLabel((Rectangle){ anchor01.x + 24, anchor01.y + 112, 120, 24 }, "Refresh Rate");
  GuiLabel((Rectangle){ anchor01.x + 24, anchor01.y + 80, 120, 24 }, "Full Screen");
  GuiCheckBox((Rectangle){ anchor01.x + 264, anchor01.y + 80, 24, 24 }, NULL, &FullScreenCheckboxChecked);
  CancelButtonPressed = GuiButton((Rectangle){ 704, 344, 72, 24 }, "Cancel"); 
  ApplyButtonPressed = GuiButton((Rectangle){ 608, 344, 72, 24 }, "Apply"); 

  GuiUnlock();

  if (GuiDropdownBox((Rectangle){ anchor01.x + 264, anchor01.y + 112, 120, 24 }, fps_str, &RefreshRatePickerActive, RefreshRatePickerEditMode))
    RefreshRatePickerEditMode = !RefreshRatePickerEditMode;

  if (GuiDropdownBox((Rectangle){ anchor01.x + 264, anchor01.y + 48, 120, 24 }, res_str, &ResolutionPickerActive, ResolutionPickerEditMode))
    ResolutionPickerEditMode = !ResolutionPickerEditMode;

  if (ApplyButtonPressed) {
    Settings.resolution = resolutions[ResolutionPickerActive];
    Settings.fps = fps[RefreshRatePickerActive];
    Settings.full_screen = FullScreenCheckboxChecked;
    update_window_settings();
  }

  return (CancelButtonPressed || ApplyButtonPressed);
}

// ...

Full code from here: codeberg.org/Sebito/collatz/src/branch/feat/settings File: src/settings.cpp

Could you also provide the Raylib version you're using? I see you're using latest Raygui, but what about the Raylib?

raysan5 commented 2 months ago

@alexlnkp @Sebbito did you try the raygui examples? It works perfectly for me, using it daily.

alexlnkp commented 2 months ago

@alexlnkp @Sebbito did you try the raygui examples? It works perfectly for me, using it daily.

The Raygui examples work perfectly as expected, the issue is that I can't personally make the GUI interactive when I'm using it in tandem with Raylib 3D rendering (so the GUI is on top of everything but I can't interacting with any buttons or anything)

alexlnkp commented 2 months ago

I suppose I should simply give the code with the problem I'm describing instead of being cryptic, so here is the project: alexlnkp/Mapper and the file that handles GUI drawing is src/editor.c

The button is rendered, but i can't click it. Hovering over it doesn't draw an outline either. Raylib version I'm using is 5.0 (Release), latest Raygui version is downloaded from the master branch via the autobuild shell script.

raysan5 commented 2 months ago

@alexlnkp please, can you provide a minimal code example that reproduces the issue? It's difficult for me to review it over a big codebase.

alexlnkp commented 2 months ago

@alexlnkp please, can you provide a minimal code example that reproduces the issue? It's difficult for me to review it over a big codebase.

I found the issue. The reason the issue occurs is because I'm calling EnableCursor(); every frame (even if it's already enabled) in editor.c, so the GUI doesn't like that, for some reason? Now, I could just make the enable cursor be called only once on the state change of my camera, but why would GUI even be troubled by a simple call to EnableCursor();?

A bit unrelated, but: I'd greatly appreciate an exposed function like "IsCursorDisabled()" or something, as the IsCursorHidden() function doesn't really tell if the mouse cursor is disabled (locked).

Sebbito commented 2 months ago

@alexlnkp @Sebbito did you try the raygui examples? It works perfectly for me, using it daily.

So i just aligned raylib with raygui (raylib built from 5.0 and raygui built from 4.0). The controls test suite runs fine so I assume it's a skill issue on my side. I tried to match my code to the test suites code tho. I just have to figure out what the problem is now. If I find out I'll report back.

Also i noticed an error when building all the tests:

make all

...
animation_curve/animation_curve.c:25:10: fatal error: ../../styles/cyber/cyber.h: No such file or directory
   25 | #include "../../styles/cyber/cyber.h"       // raygui style: cyber

This is on tag 4.0 so maybe it was already fixed.

Sebbito commented 2 months ago

I found the issue. Tl;dr: It was a skill issue on my side.

Long version: The main loop looked like this:

while (!WindowShouldClose()) {
    BeginDrawing();

    ClearBackground(RAYWHITE);

    if (settings_button()) show_settings = true;

    if (show_settings) {
      int result = open_settings();
      if (result > 0) show_settings = false;
    } else {
      DrawText("W, A, S, D to move, Shift for more speed", 15, 20, 20, GRAY);
      draw_tree(&nodes);
    }

    EndDrawing();
}

The open_settings() function is the one i posted above. So basically it reset the settings state every time it was drawn. This meant that ResolutionPickerEditMode and RefreshPickerEditMode were always false since they'd always be at their default value (false). Thus the GUI was never locked.

Sorry for the inconvenience but thanks a lot for the help!

alexlnkp commented 2 months ago

I'm not sure if I should close this issue, as it's still really easy to replicate by simply calling EnableCursor(); every frame, even though it shouldn't really affect anything GUI-related. Let me know if this issue should be closed, as it's unknown whether EnableCursor() is intentionally causing this behavior. @raysan5

raysan5 commented 2 months ago

@alexlnkp I think it can be closed, it does not seem an issue with raygui itself...