-
-
Notifications
You must be signed in to change notification settings - Fork 772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make PGui nice #1603
Comments
https://github.com/BMaxV/basic_panda3d_PGUI Here are some basic things to get started with PGUI. I will try to expand the example to include binding functions to buttons, researching how hover in / hover out works. If that's done, everything else should be mostly trivial to build from the pieces. |
What I'm seeing so far which is problematic for pgui usability:
|
More reading and notes: There are actually lots of comments in the code that are pretty good, so just reading the source works. Buttons have a Concerning keyboard and mouse input, there seem to be PGItem defines these functions, so I'm assuming there is some loop that checks if regions are entered or quit or if the keystroke happened with the mouse in the region and so on. Which, again could be rewritten if it's really as simple as
DirectGUI uses
So it's literally an arbitrary string that's somewhat hardcoded via the id and then passed through lots of functions. And then used by the event system. For |
Just had a thought, that it would be really interesting to do on the fly animations for some UI elements and those could be built into the lower level as well. We can obviously build these ourselves, but there is no reason why some of the simpler ones can't be built in. Like, periodic flashing to draw attention by animating the color value or scale of UI objects. Like a 10% increase in scale and "value" in HSV colors, on hover to make the UI more... alive? Interactive? Also a "on hover" sound would be nice. I don't think that's built in yet. |
There is panda3d/direct/src/gui/DirectButton.py Lines 45 to 46 in ecc1e30
Doing that with the interval system is more flexible. You can always inherit the widgets and extend their functionality. |
Let's focus on the fundamental features of a UI system before we discuss adding additional features. |
I played around some more with the PGUI script I linked above. I managed to get the hover_in hover_out events to work for buttons. As well as clicks. Strangely, the exact same syntax does not work for frames. All the necessary events are in I tried the scrolled frame and entry box, both of which work pretty much as expected as far as I can see. The source is again well commented or the variable names are well named, so it's not difficult to figure out what to do with them. The text_entry also has the familiar I don't think the waitbar has any value whatsoever, since you can just use scale or set frame dimensions directly anyway. Which leaves the question of how the scroll frame does the masking. |
I had a mini-brainstorm with @janEntikan after looking at BMaxV's sample and here are some thoughts:
btn = PGButton()
btn.label = "label"
btn = frame.new_child(PGButton) You might suggest we need a NodePath for positioning, but...
It might be good to also give some thought to how we might implement the following in the future, so we don't lock ourselves in with decisions that we regret later on, even though it's out of scope for now.
|
If you are interested I actually implemented such a layout system a few days ago. It's a bit dirty code wise but gets the job done. One problem with it though is how there's not a good way to get the bounding box of a directgui element, but if there is such a way for pgui should be pretty easy |
Sure, I'd be interested in taking a look. We could fix the bounding box issue, by properly implementing calc_tight_bounds for PGItem - or we could base it on the (computed) frame, which does limit the applicability of such a LayoutNode a little bit. I suppose there should ideally also be a way to grow the frame based on the available remaining space - either this could be done by scaling (which requires something clever to counteract the scale for the contained text) or we need to base it on the frame after all. |
The main issue with the bounding box is with how DirectGUI code works.
This all leads to a pretty clunky and unsound code. For While designing the system we decided that the automatic scaling is out of scope of such a tool, although it's possible to calculate margins automatically, the thing is, Panda3D does not really have the notion of a viewport, or the "hard-set width and height". There is not a way to, for example, split the aspect2d into two frames taking 40% and 60% of the app's width, and make sure the proportion is always intact, unlike something like CSS, which means auto-scaling makes no sense in this scenario. (I do like flexboxes a lot, but it's a different paradigm from what Panda currently has) The code I wanted to show is here. Not the cleanest code I've ever made, but does the thing. |
I'm against this. I would much prefer a good constructor (which is where I would look for default values too). The biggest reason is that it's not immediately obvious which attributes are available to be set and what their wording is. Is it "text", "display", "content", "label" or the same thing with capitalization? Or "button+whatever"? Because setter functions are "functions", it is obvious that they actually do something and change the object through that action. They are easy to document, I'm assuming they are also easy to keep the same for python and C. My go to negative example is blender, because they use "pos", "loc" and "position" all different depending on context. One of them was the game engine, one of them was regular 3d view, and I think a different one for vertices and objects, but I don't remember which was which. It's one of the absolutely awful parts of DirectGui that you can and have to change e.g. frame color by using
I don't think it's a pain point? Where is it a pain point? Who specifically is having this problem? Just use DirectGUI to do this, because that's exactly what it was built for. All the "initialize options" functions you have perhaps wondered about, are exactly that kind of system that crawls the parent/child tree for attributes and rebuilds things according to the parent's style. Besides, the coordinate system does the work for us. There are even methods for detecting screen resolution for us that at least I used to detect and shift around things so that they are attached "to the right side of the screen with some padding". It is very easy to build and arrange things via a loop and the programmer can even define their own curves if a simple line is not enough. Any kind of layout system preemptively needs to be flexible and easy enough to do everything and never get in the way.
This is easy, needs no specific thought and it should not set it as a default for the programmer by panda, because you cannot know in advance what Frames are going to be used as. It is easy in the sense that you can just put all the intended frames/buttons in a list, define "up", "down" and "enter", and use the same event keyword we are using for the regular event manager to activate the respective function. It's just that "enter" would dynamically pick which one that is based on where the "cursor" is in the list. I can build one if you like. The one thing that I have not seen done well yet in Direct or PGUI, and that I have absolutely no idea to build is something like these heatlhbars and for Apex everything actually: https://www.gameuidatabase.com/uploads/Agents-of-Mayhem06242021-105126-91152.jpg https://www.gameuidatabase.com/uploads/Apex-Legends07152020-072204-49086.jpg Because they require flexible textures, right now, textures are inherited from the scale of the frame, so if you rescale the frame, you also change the scaling the texture as well. Which means you "squish" the texture if you scale the frame on the x axis. Same for Frame size. If I'm wrong about this, I'd appreciate documentation on it :) And they require some kind of transparency or masking which, I've already mentioned before. Also take a look at this: https://poe.4fansites.de/bilder/guides/poe-guides.jpg The red health orb to the left, with the white energy shield over it, and the blue mana to the right.
I think those kinds of functions have to be the goal. Games can get away with lots of low fidelity art, but the UI has to be good. |
One more thing I forgot in my previous comment...
do.accept(btn.click_event, my_handler_func) Do we think this is fine, or is exposing names like "click-pg123" considered too clunky? In C++, you can create a notification delegate by inheriting from PGItemNotify, which we could extend to Python, so you can do: class EventNotify(PGItemNotify):
def item_press(self, item, param):
print("button", param.button, "clicked")
btn = Button()
btn.notify = EventNotify() That's very powerful, but that's a lot of code to do something simple... an alternative is allowing you to specify an event name, which is a bit better: btn.label = 'Start Game'
btn.click_event = 'start-game'
do.accept('start-game', start_game) We could also consider adding a btn.bind('click', start_game) |
I don't think we need a |
Items may have many properties to set. Aside from the code style arguments (which may be a matter of taste, but I find typical DirectGUI code with a dozen constructor arguments quite ugly), we're trying to make the API similar between Python and C++, which doesn't have keyword arguments.
Surely this is the same with constructor arguments or properties - in both cases you need to look up what the properties are called in the API reference. There's no capitalization if we're just consistently following code style conventions,
We're trying to replace DirectGUI here. DirectGUI doesn't have layouting features--I don't think we're talking about the same thing. I'm talking about something like a GtkBox or NSStackView or wx.Sizer or QVBoxLayout or (insert equivalent for any UI system of choice here). These don't just align elements in a row to a corner of the screen, but can also resize elements or the space between elements proportionally according to the available space in the container and chosen parameters.
Well, you can, if we allow the user to communicate this via the API. It's not rocket science to implement it yourself, but it can get annoying if you have to do it every time and it seems reasonable to make this thing easier for the user. I do think it's out of scope for this issue, I just wanted to make sure we give some thought to this so we don't limit us from creating such a feature in the future. I think the discussion of whether we actually want to create such a system in Panda should be left to a separate issue, to keep this one focused.
Yes, this deserves some consideration - achieving the result in these examples requires 9-slice scaling support and we should think about whether and how we should support that, since it's a fairly fundamental feature of UI theming. |
I was digging a bit into PGui and it turns out this is supported, just undocumented: In PGui, all these things are part of the PGFrameStyle. I think the main thing to provide themeability is to provide an easy way to access, store and apply PGFrameStyle objects. |
Regarding the health- and mana- bars shown, for myself I'd be inclined to use shaders for those: doing so provides more control over the appearance of the bar, and allows for some nice fancy effects like fluids sloshing and energy shields glowing, and so on. The one exception is that the health-bar with the shaped end could, I imagine, be implemented simply via texture-offsetting. That is, instead of scaling the entire bar, just ensure that the texture is in "clamp" mode and then offset it by the appropriate amount, creating the impression of the bar filling and emptying. ~ Regarding things like button-events, I think that I prefer the current system of assigning a function to the object's callback-variable. i.e.
It's quick, it's easy, and it's (to me at least) nicely clear. ~ Regarding the matter of layout, I'm inclined to think that while layout objects are potentially useful, it's more important to have a visual tool in which to design one's UIs. I daresay that placing things visually, having not only the ability to arrange objects by sight but also immediate visual feedback on layout, will in general tend to be far more intuitive than a code-based approach, even if that code-based approach is excellent. (If DirectGui Designer could be adapted to serve PGui, that might do the job.) Now, such a tool is likely beyond the scope of this thread, but I want to mention the above against the idea of spending too much energy on code-based layout elements. |
There are no good ways to set default fonts for directgui. rdb suggested This goes a bit into the styling problem that directgui wanted to solve a bit. I don't think the global is that bad in this case, but the question remains whether we want an object to store style info, pass it to constructors every time or solve it with a global like directgui. |
Hmm... My main qualm with a singular "setDefaultFont" is that I may want multiple styles with multiple fonts. For example, I might want a style that's used for headings, and one for text shown when the player collects an item, and another for paragraph-text. And the former two might share a font--but with different settings. Now, if "setDefaultFont" were to take a style-reference, which could then be passed to DirectGUI widgets, then that might work... ... But then, there would likely be other style-elements to be applied. Now, this could be done via multiple such globals--but that starts to become awkward and error-prone. So, I'd thus be inclined to prefer style-objects that could be passed wholesale to DirectGUI widgets. |
In the effort of nice looking buttons, the following was found: (Setting all this via text node works fine, it's part of my example, so I'm going to use that as a reference) Direct can set text color this way
But it can not set the outline this way. I did not find the appropriate keywords, if they have been setup and it's not documented. With PGUI things are weird.
Let me explain/demonstrate. I think you should have both my demo file https://github.com/BMaxV/basic_panda3d_PGUI and something to execute it ready for this to make sense. And I put some comment markers in the limiting where I will change code.
|
The other day I made a button constructor for textnode and DirectFrame, where I would remove the DirectFrame and replace it with PFrame when we have figured out how the hover_in / hover_out is solved with PGUI. Most importantly for that was that I wanted a unified style input where I can preload font, background etc. and just pass that around and have all buttons look nice. The question now is what the save file format should look like, to make it even easier to for example, share configs. I just wanted to bring it up. |
There are a couple of problems with DirectGUI:
Inevitably, every GUI-heavy application ends up writing their own wrapper around DirectGUI.
DirectGUI is based on PGui, which implements most of the functionality, but it is a bit harder to use, and undocumented.
Rather than keep investing in DirectGUI, or in an altogether new GUI system, which would be a lot of effort and add to the maintenance burden, I would suggest that we take a look at improving PGui, which does work and we are already obligated to maintain. Someone should spend some time trying to use PGui, identify the pain points, and either suggest ways we can improve its API (preferred) or write a new (thin) layer over it. Then we can document it and write sample code for it, and promote it over DirectGUI.
Initially, I would like to keep the scope of this project manageable and not try to add too many nice-to-have high-level features (like gamepad navigation), though thought should be given to making these kinds of features easier to integrate in the future.
The text was updated successfully, but these errors were encountered: