Main Menu
Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - texus

#51
QuoteWhat I'm trying to do is even correct?
Unfortunately it isn't. You can only cast to a derived class if the object was from that type in the first place. So if you create an ImprovedListBox, cast it to a base class such as ListBox or Widget to store it somewhere, then you can cast it back to ImprovedListBox later. If the ImprovedListBox was however never created, and the widget was created as a ListBox, then it can't suddenly become a ImprovedListBox.

If the derived class would only contain simple member functions they you could get it to work (even though it would be undefined behavior according to the c++ standard), but that is not the case here. The onRightClick member requires several bytes of extra storage, which weren't allocated when the ListView was constructed. And the rightMousePressed function requires an altered virtual function table for it to be called correctly. So even with undefined behavior, this cast is never going to work.

QuoteWhich's the right way to do this?

* Method 1

The gui builder can't create your custom widgets, it can only create normal ListBox widgets.
So the most obvious solution would be to create all such list boxes in c++ code instead of loading them from a form.

Maybe you can combine the two by still having a ListBox widget in the form, but looping through all widgets after loading it to replace them. You would remove each ListBox widget from the gui and add a new ImprovedListBox widget in the same spot at the same position and with the same size as the removed ListBox. Implementing something like this isn't trivial though, you have to e.g. be careful not to alter the list you are looping through.

* Method 2

Loading widgets from a file can however support custom widgets. So you can make it work by manually editing the form file with a text editor after the gui builder created it.

The form contains a line like "ListBox.ListBox1", where "ListBox1" is the name you gave the widget in the gui builder. You need to change it to "ImprovedListBox.ListBox1". Note that after doing this, the Gui Builder can no longer open the form again, it will complain that it doesn't recognize widgets of type ImprovedListBox.

The loadWidgetsFromFile function in your code also won't know how to create such type out of the box. This can however be solved by adding the following line at the beginning of your program:
Code (cpp) Select
tgui::WidgetFactory::setConstructFunction("ImprovedListBox", std::make_shared<ImprovedListBox>);

Now when loading the form file containing an ImprovedListBox, you can get it directly with gui->get<tgui::ImprovedListBox>("EntitiesList")
#52
The problem lies with the remove call in onMouseLeave.

The remove call in deleteButton->onClick wasn't an issue by itself, but it causes contextMenu->onMouseLeave to be called during the remove. Calling remove again there (while the previous call hadn't finished yet) causes the crash.

If you put "tgui::Timer::scheduleCallback([=]{ gui->remove(contextMenu); });" in contextMenu->onMouseLeave then it won't crash. It will call "gui->remove(contextMenu);" one time too much (once for the onClick and once for the onMouseLeave), but TGUI will just ignore the second remove call.
#53
I can't tell you how to fix it if I don't even know what is causing the crash. I can see a line number in the call stack, but I don't have your source files so I don't know what is on that line.

At which line in the code is it crashing exactly? I assumed it would be the "gui->remove" line, but if you added tgui::Timer::scheduleCallback and got the same call stack, then it feels like the code must be crashing at some other line.

If your project isn't too big, could you perhaps upload a zip file of it, so that I can maybe try building it here tomorrow?
Otherwise it would be better to try and reproduce this in a simple program with minimal code, that makes it a lot easier to determine where it goes wrong as well.
#54
What exactly is on line 181 in editorscene.cpp (or whatever the line is now when you get a new stacktrace)?
#55
Inside a callback function you shouldn't remove a widget that is handling the event. So the "deleteButton->onClick" callback shouldn't alter the existence of deleteButton or contextMenu (and it is trying to remove the contextMenu).

There are some exceptional situations where it actually works fine, and when looking at the code I expected this to be one of them. Since it crashes when you add the remove call, it however looks like the remove won't work in this context.

One solution is to delay the execution of the line by placing it in a timer. Simply replace the "gui->remove(contextMenu);" with "tgui::Timer::scheduleCallback([=]{ gui->remove(contextMenu); });". This will cause the contextMenu to be removed at the next gui.draw() call instead of immediately while still handling the mouse release event.
#56
A call stack would be useful for this as it would show where it crashes. You should be able to get one when you press the "Retry" button in that assertion popup.

When does the error happen exactly? When you click the delete button? If you put all the code in the "deleteButton->onClick" callback in comments, does the crash still occur? Does it occur if all the code except for the "gui->remove(contextMenu);" is placed in comments?
#57
onItemSelect is of type SignalItemHierarchy, which only supports a parameter of type tgui::String or std::vector<tgui::String>.

I'm surprised that it is even possible to connect a function with a tgui::Vector2f parameter, but apparently this is because a Vector2f can be created from a string. The string to initialize Vector2f needs to have format "(x,y)", while the element passed to the onItemSelect is the text of the clicked item, which is why you get the "Failed to parse Vector2" error.

In this case you aren't using the "elem" parameter, so you might as well not give the callback function any parameter.

QuoteI noticed that the Panel is not being generated

I tried your code and the panel is being created, just not on the location where you clicked. For you that location might have been outside the window which is why you wouldn't see it.

"sf::Mouse::getPosition()" returns the position on the monitor, you want "sf::Mouse::getPosition(window)" instead. After changing that, the panel did get created at the right location.

After running your code I noticed that it crashed in some cases. The lambda passed to onMouseLeave should have "[=]" instead of "[&]" (and you probably want to do the same for onItemSelect, which is possible since gui is a pointer anyway). While the panel continues to exist inside the gui, the "contextMenu" variable is destroyed when onItemSelect finishes. When the onMouseLeave callback is executed, the variable no longer exists so it crashes. With [=] you aren't refering to the variable anymore but instead are making a copy. In that case, when onMouseLeave is executed you still have a valid pointer to the panel that you can use.
#58
Code (cpp) Select
label->getRenderer()->setTextOutlineThickness(1);
label->getRenderer()->setTextOutlineColor(tgui::Color::Black);
#59
Are those the only linking errors, or just the first 10 out of a much longer list?

Can you try with placing the "$<$<CONFIG:Debug>:tgui-s-d>" line to above the "$<$<CONFIG:Debug>:sfml-graphics-s-d>" line? Especially for static libraries, you typically have to order the list so that if A depends on B then A should appear before B in the list.

If that doesn't fix it, then can you tell me which compiler and SFML and TGUI versions you are using?

Normally in CMake you would use find_package to search for SFML and TGUI and then you would only need `target_link_directories(BreadEngineCore PRIVATE sfml-graphics TGUI::TGUI)` to import all settings such as include directory, linker path, defines and dependency ordering. However the manual method that you are using should also work fine.
#60
Copying tgui::Texture objects or setting them in a picture are light operations.
What is a very heavy operation (other than loading the image from disk) is to create a tgui::Texture object from an sf::Texture one. So calling setTexture with an sf::Texture each time would be a bad idea.

A collection of pictures would be fine, but having a list of tgui::Texture objects and then calling setTexture on a single picture would have slightly less overhead.
#61
Yes, that would be the solution.
You would indeed need a second function with tgui::Container::Ptr, unless you make the first parameter a template as well (but then you will need to pass "&gui" as argument when calling the function).
#62
I've given it some more thought and asked for some extra opinions about this issue on discord yesterday, and my decision is that I'm not going to change the API for this. I don't really want to add another function and extra code just to provide slightly better debug information in the case where the "get" function is used without proper error checking (i.e. testing if returned value is nullptr or not).

QuoteI can also do this in case of an exception being thrown, can't I?
Yes, you could catch the exception to find out that that the widget didn't exist. So no functionality would be lost. The code wouldn't look as clean as a simple if check though.
#63
The issue is that calling the get function with an incorrect name isn't technically an error, it could be intentional. Otherwise it would be simple: I would just add an assert in the get function, that would immediately alert the developer that he's doing something invalid.

Maybe I could consider some global flag that you could set to cause a get() function to crash when used with an invalid name, but I would first need to verify that TGUI isn't relying on a nullptr being returned internally.

In TGUI 0.6 I had my own smart pointer type instead of using std::shared_ptr. That actually had one big advantage: I could control what happened when attempting to dereference a nullptr. I don't think it's worth going back to something like that, but it would allow better debug information as well. This is however something that I feel the debugger should catch for you. Similar to how "operator[]" asserts in debug mode but not in release mode, I feel like a nullptr dereference should be something that the debugger very clearly tells you about when running the code in debug mode. Some debuggers do a better job than others at showing where the real issue lies in these cases.
#64
The thing that I'm a bit worried about is that it might not always be as useful to throw there. If you have a try-catch in your main function to print the exception and then return from main (which I sometimes even recommend that people do), then the debugger won't tell you where the exception occurred. Although if I include the widget name in the exception error then you would probably still be able to locate the faulty "get" call.

The usability of detecting an issue by throwing an exception really depends on the platform. On my linux system, any assertion or uncaught exceptions will be printed out in the terminal with the error message, even without a debugger attached. On other platforms, it just crashes without printing a message. I've had multiple people ask me in the past why there code was crashing where it turned out to be an uncaught exception, which is the reason why I often recommend the try-catch in the main function: so that these people would be able to see what is going wrong. The exact same advice is however bad for people who do use a debugger properly, as they get a better output without catching the exception.

The "get" function isn't going to change for backwards compatibility and because I'm not convinced that it should throw (because I try to only throw things that might be called during initialization). So the only thing that I can do is add a new "tryGet" function. The problem with that is that you need to change your code to always use the new function everywhere, and I'm not sure if it really helps much. The debugger will stop inside the "tryGet" function on failure, so you would still need to get a call stack to figure out where the function was called. The exact same "bt" command in gdb would probably also tell you that there is a nullptr in "pic->getRenderer()->setTexture(texture);" though. Although I admit that the call stack with the exception would be slightly more useful if you don't know what to look for.
#65
The "get" function isn't guaranteed to succeed, the documentation states that it will return a "nullptr" if it can't find the widget.

The only thing that I could do to improve this situation is to create an alternative getter that would throw an exception instead of returning a nullptr.

Does the debugger give you an address when the segmentation fault occurs? It should, and the address should be very close to 0 for this crash. If you see this, then the first thing to check is if you are calling a function on a nullptr. This can be helpful to more easily find what is causing the crash.

Another useful thing to look at if you get a crash is the call stack. If you have the debug symbols for TGUI then it might even literally contain that the this pointer is 0x0 inside the Picture function.
#66
TGUI doesn't just support SFML, it can also be used with e.g. SDL or GLFW. I designed the library so that it could be built with multiple of these backends simultaneously and that the backend would be selectable at runtime. The downside of that is that until the backend code is loaded, TGUI has no idea which implementation of tgui::Texture it should use (loading the image is completely different in SFML vs SDL).

So somewhere at the beginning of your code you need to initialize the SFML_GRAPHICS backend after which you can actually use TGUI (as loading the backend initializes the internals of the library). While you can manually create the backend object, for simplicity it is tied to the Gui object by default. If you create the Gui object, it will automatically initialize the backend if no backend was loaded yet. This however means that unless you manually initialize the backend, you must load the Gui object before you do anything else with the TGUI library.

Essentially creating and destroying the Gui object acts like global TGUI_Init() and TGUI_Destroy() functions.

One extra detail to note: if the Gui object is created WITHOUT a window, then the backend still won't be initialized until the gui is given a window, so a window needs to be attached to the gui before you can load the texture (because only then does TGUI have access to the OpenGL context from the SFML window).
#67
That assertion means you are trying to create the Texture before the Gui has been constructed.
#68
You can temporarily disable the onValueChange signal:
Code (cpp) Select
SpindleControl2->onValueChange.setEnabled(false);
SpindleControl2->setValue(BalancedValue);
SpindleControl2->onValueChange.setEnabled(true);
#69
The most efficient way is to calculate it yourself on a resize event, but you can actually let TGUI do this automatically.

If you know the ratio is 4/3, you can use this code:
pic->setPosition("(&.size - size) / 2");
pic->setSize({"min(&.w, &.h * 4/3)", "min(&.h, &.w * 3/4)"});

Or if you don't know the ratio you can calculate it on runtime and change the above setSize call to the following (this code assumes the picture is added to the gui directly, but just replace "gui" with the parent of the picture if not):
pic->setSize({bindMin(bindWidth(gui), bindHeight(gui) * (pic->getSize().x / pic->getSize().y)),
              bindMin(bindHeight(gui), bindWidth(gui) * (pic->getSize().y / pic->getSize().x))});
#70
Installation help / Re: CMake cannot find SFML
31 March 2024, 16:02:24
Quotebut I couldn't find anything. Is it possible that the file is not in the folder and CMake creates it?
The file has to be created by CMake somewhere in your build directory. Deleting the entire build directory should cause it to be recreated from scratch instead of from cached files.

The most important thing is that you got it working now though.
#71
Installation help / Re: CMake cannot find SFML
30 March 2024, 08:59:34
I have no idea how it's even possible, but something looks wrong with that TGUI dll. The size of your dll is smaller than the ones I have. Even the 32bit dlls that I have are still larger than the tgui-d.dll that was generated on your pc.

I've downloaded the "MinGW-w64 13.1.0 - x86_64‑posix‑seh (64bit)" version of TGUI 1.2.0 from https://tgui.eu/download/ and copied the tgui-d.dll from the bin folder of the zip file into your build_directory and afterwards I could run your exe just fine (after also copying xubuntu_bg_aluminium.jpg into the build_directory).

EDIT: Your dll file isn't even recognized as a valid DLL by the tools that I have to dump dll information.
EDIT 2: The first 1536 bytes (= exactly 3 * 512 bytes) in your DLL are all 0-bytes, so the file looks corrupted. Have you tried deleting the file and letting your cmake project generate it again?
#72
Installation help / Re: CMake cannot find SFML
29 March 2024, 08:29:09
I'm not sure how much I can help with this issue.

One thing you could still try is to use the precompiled TGUI libraries for MinGW 64bit instead of building them yourself, although I doubt that it will solve anything.

Have you tried reinstalling the latest Microsoft Visual C++ Redistributable version just to be sure?

Could you create a zip file with the exe, SFML dlls, TGUI dll and the 3 gcc dlls that I mentioned in my previous post and send those to me? Then I can try testing if I find anything weird with it.
Could you also post the current CMake script that you are using? Maybe I can try building the same way here and see if I can reproduce it or not.
#73
Installation help / Re: CMake cannot find SFML
28 March 2024, 19:28:05
0xc000012f seems to mean that the "Microsoft Visual C++ Redistributable" isn't installed, which is a bit weird in this case. To run any software that was build with Visual Studio, you need to have the redistributable installed for that Visual Studio version. It raises a question though: what code is built with Visual C++? You are building with MinGW, the precompiled SFML libraries were build with MinGW, and TGUI is being build together with your project and also with the MinGW compiler.

Is the bin directory of your MinGW compiler in your PATH environment variable?
Just to be safe, can you copy libgcc_s_seh-1.dll, libstdc++-6.dll and libwinpthread-1.dll from "C:\mingw64\bin\" and place them next to your exe?
#74
Installation help / Re: CMake cannot find SFML
28 March 2024, 18:32:01
Somewhere inside your "c:/Users/luka-/OneDrive/Desktop/AutoBackupScript2/build" folder. Probably inside the "TGUI/lib/Debug" subdirectory.

There is a way to let CMake automatically copy the dll when building. Try adding the following to your cmake script: (I didn't test this so it might not work as-is)
Code (cmake) Select
add_custom_command(
    TARGET AutoBackupScript POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:tgui>" "."
    VERBATIM)
#75
Installation help / Re: CMake cannot find SFML
28 March 2024, 08:39:33
QuoteCould I be overlooking something, such as copying .dll files or a similar requirement?
It's possible, but it's hard to say if you don't actually get an error message. Are the sfml and dll files placed next to the exe?

I don't really know VS Code. Do you have some process output somewhere? If the program crashes then it usually sets a return value other than 0. If you find some output with something similar to "program exited with code ..." then you can usually search online for that code to determine whether it ended due to a missing dll, an access violation or an uncaught exception.

There are 2 things you can test:
1) Run the exe from file explorer instead of from the IDE. If it won't run due to a missing dll, you will get an error when attempting to run the exe like that.
2) If something goes wrong in TGUI (e.g. a file can't be loaded), then it will throw an exception. If you don't catch this exception then the program terminates.

Do something like this in the main function to print something to the command line when an exception occurs, so that you can see what went wrong. I'm not sure if you will be able to see the text printed somewhere, but you might also be able to e.g. put a breakpoint in the catch code to test if it passes there. The "e.what()" value is a string that contains the TGUI error message.
Code (cpp) Select
int main()
{
    try
    {
        // <Put your existing code here>
        return 0;
    }
    catch (const tgui::Exception& e)
    {
        std::cerr << "TGUI exception: " << e.what() << std::endl;
        return 1;
    }
}