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

#151
Help requests / Re: SpriteSheet in v1.0
04 March 2023, 10:56:33
setVisibleRect isn't what you need. It was added to the sprite just for being able to partially display the loading bar image, it effectively clips part of your sprite (so the size of the sprite would still be the entire loading bar while only part of the bar is shown).

Sprites and textures do work a bit differently in TGUI than in other libraries. I think originally the current Sprite and Texture classes were even a single class.
A sprite always shows the entire texture.
A texture on the other hand may contain only part of an image.

What you need to do is load multiple Texture objects, with the same filename, but with different partRect parameters.
That may sound very inefficient, but it works because TGUI will internally reuse the texture when loading the same filename multiple times. So you would end up with only one internal sf::Texture (containing the full 2000x2000 image), and multiple tgui::Texture objects that refer to (part of) the same resource.

All rectangles used by sprites and textures use pixel ranges.
If your image is 2000x2000, the PartRect of your Texture is (0,0,50,50) and the sprite size is 100x100 then the top 50x50 part of your image will be 2x scaled and shown as 100x100 on your screen.
#152
I would just recommend copying the code from the Tabs class and editing that copy.

You could technically inherit from Tabs, but then you would need to store a list of images that has the same length as the list of tabs. So every function that adds or removes a tab needs to be overridden in your new class (and these functions aren't virtual, so you need to understand the limitations of doing this and watch out for them).
#153
Help requests / Re: ToolTip on click
14 February 2023, 18:32:12
setToolTip is used to tell the Gui that the widget has a tool tip, so that it can be shown when the mouse stands still on the widget for a while. Calling setToolTip won't actually display anything.

You could call tgui::ToolTip::setInitialDelay(0) at the beginning of your code to make tool tips appear without a timeout, but I'm not certain if it will work because tool tip might be suppressed while the mouse button is down.

I suggest you show the bubble manually, instead of the existing tool tip system, because what you want is different from how tool tips typically work. Something like the following:
- When you press the mouse button, set the position of the bubble and call "getParent()->add(bubble)"
- When you receive a mouse release event, call "getParent()->remove(bubble)"
- In your constructor, set m_draggableWidget to true so that your widget keeps receiving mouse move events while the mouse is pressed, even when the bubble is below the mouse.
- Optionally move the bubble on mouse move events while m_mouseDown is true
#154
Feature requests / Re: Menu with checks/checkbox
30 January 2023, 18:25:24
It's a feature that I also want in the future, but it isn't supported currently.

You might be able to hack it together yourself by altering the text:
addMenuItem(U"\u2610 Unchecked")
addMenuItem(U"\u2611 Checked")
addMenuItem(U"    Normal")

And then use changeMenuItem later to alter the first part when checking/unchecking.
#155
Help requests / Re: Tree View Tutorials?
17 January 2023, 08:15:02
There are no examples in the documentation or tutorials yet. A hidden example can be found in the tests: https://github.com/texus/TGUI/blob/0.10/tests/Widgets/TreeView.cpp#L372-L378

So you have to add 2 items: {"Object", "Subobject"} and {"Object", "Subobject2"}
#156
Most functionality about how a widget looks is now part of the renderer (which can be accessed with "getRenderer()"). So you can change it with "panel->getRenderer()->setBackgroundColor(...)"
#157
In the latest TGUI 0.10-dev version the handleEvent function will now return true when scrolling while on top of a widget.
#158
I never thought about that case before, for that use case the old behavior indeed worked a lot better.

I've looked at the behavior in Firefox again on both Windows and Linux and this is what I intend to implement for nested scrolling now:
- When you start scrolling, a panel remembers which widget is getting the scroll events and "locks" it.
- Any scroll events that occur while a widget is locked will be passed to the locked widget instead of the widget below the mouse.
- If no scrollbar has moved for one second, the scrollbars are unlocked. (The time seems much longer than 1s in Firefox, but I'm going to start with just one second in TGUI and determine if it needs to be changed later)
- If a mouse move event is received, the scrollbars are unlocked.

While that would fix nested scrollbars, it might not yet solve your situation. For your case there are several options:
1) Do nothing. Right now the behavior is a big problem for you because the view changes immediately when the scrollbar reaches the tip. If I implement the locking, the scroll event will be absorbed for a second (and handleEvent will still return true for scroll events that happen immediately after the scrollbar reached the tip). If the scrollbar is already at the tip, and the user then moves its mouse on top of the panel and starts scrolling, is it really an issue if it zooms your view? If so, then I will need to implement one of the other options.
2) Make handleEvent always return true for mouse wheel events when the mouse is on top of a TGUI widget. This is probably easy to implement and probably works well for most people. But it would also absorb scroll events when the mouse is e.g. on top of a button or a non-scrollable panel. If this becomes a problem for other people then I could add a boolean in the gui to decide on what behavior to use.
3) Adding a boolean to the ScrollablePanel to make it absorb all scroll events. I don't like this option simply because ScrollablePanel isn't the only widget that can handle scroll events, I would need to do the same for widgets like Label and TextArea as well.

So if you need the behavior then I will implement the second option, otherwise I will go with the first option and only fix the scrolling that occurs directly after the scrollbar reaches the tip.
#159
Creating a custom widget that inherits from ScrollablePanel and overrides the mouseWheelScrolled would be an option, but I feel like this is a behavior that can still be improved in TGUI itself (e.g. by adding a boolean flag to the widget to allow you to choose the behavior).

The behavior of passing the scroll event to the parent is actually rather new, it was only added 4 months ago and I knew someone was going to ask for a different behavior at some point (and I decided to wait with "fixing" this issue until someone actually needed a fix).

There are two behaviors that are easy to implement, and I like neither of them:
- Absorb all scroll events. You won't be able to scroll the outer scrollbar while the mouse is on top of the scrollable panel. This is what you are asking and this is the old behavior. I've worked with interfaces that work like that and I really disliked it: you can get into situations where you are scrolling the outer panel and suddenly the scrolling stops because your mouse is now on top of another panel and you have to move the mouse to continue scrolling.
- Pass event to parent when the panel can't scroll any further. This is the current behavior. The big downside is that you practically always end up scrolling too far and end up scrolling the outer panel while you just wanted to go to the end of the inner panel.

I've looked at how this is handled in other applications when I changed the code 4 months ago. What is typically done, and what I consider the correct/best behavior, is too not pass any events to the parent for a short time after scrolling. When the scrollbar reaches the end of the inner panel and you keep scrolling, nothing happens. But if you stop for a second and then start scrolling, the outer panel will scroll even when the mouse is still located on top of the inner panel. This is unfortunately harder to implement.

Which brings me to my question: do you really want to fully disable scrolling of the outer panel while the mouse is on top of the inner panel? Or would it also work for your use case if the events where only suppressed for a short time after scrolling the inner panel?
#160
The issue has been fixed (github commit).
Your code should work now with the latest 1.0-dev version.
#161
I figured out what is going on, but I will look into the details and check if I can do something against this tomorrow.

ScrollablePanel::setSize is looping over its bound layouts to update them. This is where your ItemContainerWidget::setSize function is called. Here you call "newSize = size" which copies the layout. During the copy, the layout registers itself to ScrollablePanel. The problem is that ScrollablePanel is still looping over the list of bound layouts, which is now being changed. This causes it to get corrupted and crash.

So for now its forbidden to copy the layout (i.e. the "size" variable) inside the setSize function.
#162
I can reproduce it here consistently, it happens immediately when I resize the window.
On first sight this looks like a bug in TGUI itself, it looks like the layout somehow gets corrupted, it doesn't look like a recursion issue.

It only crashes when copying "size" to the "newSize" variable (even if you don't change newSize.x) and only when the size contains a math operation (i.e. binding just the height of scrollable panel works, but when subtracting a constant from it such as the scrollbar width causes it to fail). So my guess is that it triggers some edge case in the layout system.

I will investigate this further and try to figure out where the crash is coming from tomorrow. Thanks for providing the simple code example that allowed me to quickly reproduce the issue.

While testing I did find another unrelated crash:
If you make your window too small, the size becomes negative (because the scrollable window height is less than the scrollbar height). In this case you call text.setCharacterSize with a negative number, which becomes a very large number because the character size is unsigned. This causes TGUI to crash while loading the font glyph to figure out the size of the text.
#163
Quotethe fact that I change the size before passing it to Widget::setSize could be the problem
No. I saw you were doing that earlier, but there is no reason why it wouldn't work.

QuoteHow it is managed with Label and its auto size feature ?
As long as you don't call setSize on the label, changing the text will trigger an update to the size at src/Widgets/Label.cpp#L699
So no layouts are used for the auto-sizing, it just recalculates the size each time the text is updated.
#164
m_boundSizeLayouts being endless might imply data corruption. If somehow a callback gets triggered to a widget that no longer exists, then m_boundSizeLayouts would contain garbage and it could crash there. But I guess there are probably ways to get stuck there without corruption as well.

The parent size is changed first. The order of events is as follows:
- You resize the window
- The gui view is updates, which updates the size of the RootContainer (which is the part of the Gui to which all widgets are added)
- m_scrollPanel's size is part of the m_boundSizeLayouts of the RootContainer (because you called m_scrollPanel->setSize("100%", "20%")), so RootContainer::setSize calls recalculateValue which calls ScrollablePanel::setSize
- ScrollablePanel::setSize will call Widget::setSize to begin applying its size
- The layout of itemContainerW is bound to ScrollablePanel, so this will call ItemContainerWidget::setSize (via the recalculateValue call again).
- Your ItemContainerWidget::setSize function should call Widget::setSize like you do in the code you showed
- The m_boundSizeLayouts of your widget should be empty, if no other widget has bound the size of ItemContainerWidget, so no further calls are made.
- After Widget::size finished, your ItemContainerWidget::setSize function you can rely on the m_size variable to contain the current widget size.
- Each mentioned function will now finish in opposite order, i.e. the remaining code of ItemContainerWidget::setSize will be executed before setting the size of ScrollablePanel in ScrollablePanel::setSize finished.

So one thing you could still test is to check that m_boundSizeLayouts is really empty in ItemContainerWidget::setSize.

If you replace "itemContainerW->setSize(800, tgui::bindHeight(m_scrollPanel)-m_scrollPanel->getScrollbarWidth());" with just "itemContainerW->setSize(800,600)", does the program work fine?

I don't see anything wrong based on your descriptions, there is probably some small mistake somewhere. Could you perhaps share your code so that I can try to reproduce it here?
#165
QuoteIf this is a circular dependency issue, why is the crash not happenning every time ?
One explanation would be caching and rounding errors. Imagine if widget A becomes 100px and it tells object B to become 200px (because it used bindSize(A)*2 as its size). Now imagine A having a size defined as bindSize(B)/2. When B becomes 200px, it will tell A that is should be 100px in size. Instead of creating an endless loop, A will already be 100px and will ignore the event.
If A was set to a size of bind(B)/4 then the size would keep changing and you get an infinite loop. But even if it uses /2, if due to a float rounding it becomes 99.99999 instead of 100, it could also trigger another loop. So with certain numbers everything would be fine, but with other numbers you would get a slight rounding error and suddenly things would go wrong.

That is the theoretical explanation as to why it could be random. I however don't actually believe that what I described is happening.

Quotethink it's recursion issue as there is no call stack, no error output just crash. In debug mode of vsc, the app stays without any response, it's another hint that's recursion issue.
Recursions usually do end up with a call stack, as you get a crash at a maximum recurse depth (which happens almost immediately when you have an endless recursion).
If you see no crash but the app becomes unresponsive, then maybe it entered an endless for or while loop? When it hangs, is the program still running? If so, can you pause the program with the debugger at that time?
#166
The only reason for the layout system to crash that I am aware of, is if you create a circular dependency. For example if the height of m_scrollPanel is changed in your setSize function or is bound to the ItemContainerWidget.

Do you get a call stack when it crashes? Does it crash inside the setSize function?
#167
Installation help / Re: Problem at make install
21 December 2022, 08:17:56
Which MinGW version are you using?

There are many MinGW variants on Windows. TGUI only supports MinGW-w64 (which is available for both 32bit and 64bit despite what you might think from the name), and the TDM GCC one that is based on MinGW-w64. The _wfopen_s function is defined in MinGW-w64 as well. I recommend a MinGW-w64 version from https://winlibs.com/#download with MSVCRT runtime (UCRT runtime isn't compatible with SFML).

If you use "GCC 11.2.0 + MinGW-w64 10.0.0 (MSVCRT)" from the WinLibs website then you can get a compatible SFML version from https://artifacts.sfml-dev.org/by-branch/2.6.x/

The official MinGW version (from which MinGW-w64 branched from a long time ago), indeed doesn't provide a _wfopen_s function, so it currently isn't supported.
#168
Help requests / Re: RAM and CPU time consumption
20 December 2022, 11:44:19
QuoteIs there a way to pass sf::Texture to tgui::Picture without copying the data
No.

But if this is what you are doing then you may already be able to reduce the memory usage and maybe even improve performance by letting TGUI load the texture. Instead of passing the sf::Texture to TGUI, just pass the filename of the texture to load. If you are also using the image in your own program then it will be loaded twice (once by you and once by TGUI), but passing the same filename to TGUI multiple times will result in the cached image being used instead of it being loaded again (as TGUI internally caches all textures based on the filename for as long as there is still a widget that uses the texture). So TGUI will never have the same texture in memory multiple times.

If you pass an sf::Texture to TGUI then it can't cache it, it will copy it every time you pass the texture so you end up with much more duplicate textures in memory (because TGUI doesn't realize it has been given the same texture each time).

Quoteif I understand it well, according to your tutorial, InventoryWidget will be subclass of tgui::Widget. I redefined functions I need and in draw function I can reuse (I mean copy appropriate parts of code) the draw function of ScrollPanel, drawing my custom parts inside directly with the BackendRenderTarget reference.
Yes, that is how I would do it. It's more work because you have to implement more functions (but those can mostly be copied from existing TGUI widgets), but then you only calculate and draw what you ned.
#169
Help requests / Re: RAM and CPU time consumption
20 December 2022, 08:34:37
QuoteSprite store a reference to a texture but Picture copies the texture, that why the RAM went up
This is incorrect, unless you are passing an sf::Texture to the Picture (in which case you have the same issue with Sprite). The tgui::Texture class is a lightweight wrapper that points to the texture recource. As long as TGUI loaded the texture (i.e. you passed a filename to TGUI instead of an sf::Texture), the texture resource will internally be reused.

Btw, I'm talking about tgui::Text and tgui::Sprite. You mention that Sprite stores a reference, so maybe you are looking at sf::Sprite instead of tgui::Sprite then? TGUI mostly does not use SFML classes internally, it has its own classes which then get rendered with sf::VertexArray when using the SFML_GRAPHICS backend.

QuoteIf I add as member of this class sf::Sprite and sf::Text, how will I manage drawing my items in the ScrollPanel with the offset of scrollbar and without going over the edge ? Is sf::RenderTexture a good idea ?
While it could be done, I would not recommend bringing SFML rendering into this. You would have to render the sf::Sprite and sf::Text to a tgui::CanvasSFML widget, which internally uses an sf::RenderTexture as buffer.

QuoteI really want to have ScrollPanel features so I made a class InventoryWidget, subclass of ScrollPanel but now I am struggling to find the right way to handle item inside
I'm not sure if it is a good idea to subclass ScrollablePanel, if all you need is a vertical scrollbar. But you can use it and just reuse the existing scrollbar. The alternative would be to copy multiple parts of the ScrollablePanel widget into your own custom widget, so I guess using ScrollablePanel makes sense as it is easier to set up.

The draw function of ScrollablePanel can't be used directly because it can only position widgets. You have to implement a draw function in your custom widget, but it is very similar to the one found in ScrollablePanel (https://github.com/texus/TGUI/blob/0.10/src/Widgets/ScrollablePanel.cpp#L615-L662).

Quotehow will I manage drawing my items in the ScrollPanel with the offset of scrollbar and without going over the edge
Have a look at these lines for an example: https://github.com/texus/TGUI/blob/0.10/src/Widgets/ScrollablePanel.cpp#L648-L654

The target.addClippingLayer and target.removeClippingLayer calls define an area to draw in, everything outside would be clipped, so the second parameter to addClippingLayer should be something like "{0, 0, width - scrollbarWidth, height}".

With states.transform.translate you move all your rendering calls to a different location. By passing the scrollbar value, all draw calls behind this line (those that are given the "states" as parameter) will be shifted by the scrollbar.

Other than that you need to use "target.drawFilledRect", "target.drawSprite" and "target.drawText" lines on the place where the example code above has "Container::draw(target, states);".
#170
Help requests / Re: RAM and CPU time consumption
19 December 2022, 20:02:21
I think you can expect it to be rather linear, but if you really need that many objects of type ItemOnUIWidget then I don't recommend making it a widget.

Realistically you would never have more than a few hundred widgets on your screen, so memory consumption or performance of creating widgets is usually not important.

Internally TGUI doesn't use the Picture or Label classes to render things, widgets use the Sprite and Text classes directly. So if you need an object with many text objects and pictures then I would suggest turning that big object into one custom widget, and have that one widget render all components in it.

Classes such as SubwidgetContainer are nice to get something simple working, but if you really need the performance then you will have to create something on a lower level. You can look at existing widget to get some idea, in e.g. the EditBox::draw function you can see that it is rendering all rectangles and texts directly to the render target and it isn't using subwidgets to do so.
#171
Help requests / Re: Enabled
18 December 2022, 22:26:47
The 'Enabled' property has no influence on whether or not a widget is shown, the description is badly formulated. Disabling a widget simply means that you can no longer interact with it.

That line in the tutorial is only correct under the assumption that Visible = 1, otherwise it will never be drawn. The Enabled property has no effect when the widget is invisible though (as an invisible widget already can't be interacted with).

QuoteHowever, if I set a widget enable to false, this also makes my widget no longer visible. Is this intended?
That might be a bug. I quickly tried creating a Knob and setting its Enabled property and it seems to work fine here: the knob remains visible but its value can no longer be changed by clicking on it with the mouse (but it can still be changed by calling the setValue function).

Do you do anything special with the knob?
If it isn't drawn for no clear reason then maybe somehow the color used for the disabled widget are set to transparent, but that shouldn't happen by default.
#172
Nice. Thanks for looking into this.
#173
I can't immediately think of anything when loading, but there is one thing in the Gui Builder that can probably leak some widgets: callbacks with lambdas.

In TGUI code I try to be careful with callbacks, but for the Gui Builder I think I didn't bother to actually check for potential memory leaks. There are 3 cases that can easily leak:
- Self reference: "button->onPress([=]{ button->...(); });"
- Referencing parent: "button->onPress([=]{ container->... });"
- Circular references: "button->onPress([=]{ editBox->... }); editBox->onTextChange([=]{ button->... });"

If the widgets are accessed by reference instead of by value in the lambda then it wouldn't matter (but then you have to make certain the captured variable doesn't go out of scope before the lambda gets destroyed). With passing the widget by value, the callback stores a copy of the widget. If you have a problematic reference like the 3 cases above then the widget won't be destroyed before its callback is destroyed, but the callback will typically only be destroyed when the widget gets destroyed.

One way to break the circular dependency is to pass a raw pointer to the lambda: "button->onPress([b=button.get()]{ b->...(); });"
But that would need to be looked at on a case-by-case basis, it shouldn't just be done on all lambdas.

It might not be the reason for the particular memory leak you found, but that is one way of easily leaking memory that I'm aware of.

I'll try to look into this memory leak myself as well when I find some time.
#174
Quoteone of my .dll files was incorrect like tgui-d.dll ?
That's the only thing that I can think of, some dll file which didn't exactly match. It's good that it is fixed now.
#175
I can't help with that, because I've never done anything similar.
The one with "(install)" will likely run the install instructions from CMake which you probably don't need to do (but it will likely run all the stuff from the non-install target first, so there wouldn't be much of a difference between the two targets).

I rarely touch the gui builder on Windows, and the way I develop it on Linux isn't usable for you, but what I usually do on Windows is to just run CMake in the TGUI root folder. Not via Visual Studio, but by just using the CMake GUI and setting the source directory to the TGUI root (and a build folder to a new directory). As generator I select Visual Studio. After pressing configure, setting the correct values (e.g. SFML_DIR), pressing configure and generate, you can open the project created in the build directory (from CMake with the "Open Project" button).
This gives you a VS project that builds TGUI and the Gui Builder. In the Solution Explorer on the left side in VS you can set the gui-builder project to be the active project so that you can run it.

Unfortunately, because I never optimized for developing on Windows, the gui-builder target isn't actually designed to run this way out of the box. If you link dynamically it will complain about dll files, you have to copy them next to the gui-builder.exe file that gets created in some subdirectory of your build folder. Even when the gui builder launches, it will immediately terminate. This is because it can't find its resources. You have to also copy the "resources" and "themes" directories, that you find in the gui-builder directory (in TGUI root), next to the gui-builder.exe file. After that you should be able to correctly run the program from inside VS.

Edit: I'll have a look at your PR tomorrow btw.