Resizing a DirectGuiWidget doesn't update position of text

Hi,

In my attempt to create an automatic layout system for DirectGui widgets, I stumbled upon a couple of issues, including this one:

whenever I resize a DirectGui widget (using widget["frameSize"] = new_size), the position of center- or right-aligned text doesn’t change.

Is there some way to force an update of the text position?
Accessing the TextNode used for the text (by calling widget.guiItem.getTextNode()) and calling setFrameActual(*new_frame), setAlign(TextNode.A_center) and setText on it doesn’t seem to work.
If this is not the TextNode I need, then I’m not sure how to get to the one used specifically by a certain widget (or if that would even help me at all).

Thanks for any help!

For the sake of others coming into the thread, and for the sake of completeness in it, let me start by quoting my explanation of this behaviour:

I think that there’s a caveat here: changing the frame-size doesn’t actually affect where the button thinks its “left”, “right”, and “centre” are. Instead, it’s the other way around: the frame-size seems to be specified relative to the text-anchor.

You can see this in action if you construct buttons with left- or centre- anchors, then change their frame-sizes such that they have a left-extent of zero. (Which should thus tell you where the buttons think that zero is.) You should find that in buttons with a left text-alignment, the left-hand extent of the button ends up at the left of the text, and that in buttons with a centre-alignment, the left-hand extent ends up at the centre of the text.

It’s arguably not intuitive, but it does seem to be the way that it works. ^^;

Original link.

That said, I see two potential workarounds:

  1. You could perhaps alter the applied frame-size based on the assigned alignment.
    • That is, if the widget is set to be centre-aligned, you could offset the horizontal elements of the frame-size such that they’re centred on zero. Similarly, if the widget is set to be left-aligned, you could offset the horizontal elements of the frame-size such that zero is on the left of the frame.
  2. You could perhaps set the “text_pos” keyword to offset the text as appropriate.
    • That is, if the widget is set set to be left-aligned, you might set the horizontal element of the “text_pos” keyword to the left-most extent of the new frame-size.

Quick examples of these approaches:

(1)
(As it turns out, it seems that you can’t directly query the “text_align” keyword at the moment. I’ve opened a GitHub issue for this matter here.)

from panda3d.core import TextNode

from direct.showbase import ShowBase as showBase
from direct.gui.DirectGui import DirectButton

class Game(showBase.ShowBase):

    def __init__(self):
        showBase.ShowBase.__init__(self)

        self.accept("escape", base.userExit)
        self.accept("space", self.resize,
                    extraArgs = [(-2, 2, -1, 1)])

        self.btn = DirectButton(text = "Kitten",
                                text_align = TextNode.ALeft,
                                scale = 0.07)

    def resize(self, newFrameSize):
        print ("Resizing")

        textNode = self.btn.guiItem.getTextNode()
        print (textNode.align == TextNode.ALeft, TextNode.ACenter)

        if textNode.align == TextNode.ALeft:
            width = abs(newFrameSize[1] - newFrameSize[0])
            newFrameSize = (
                0,
                width,
                newFrameSize[2],
                newFrameSize[3]
            )

        self.btn["frameSize"] = newFrameSize

app = Game()
app.run()

(2)

from panda3d.core import TextNode

from direct.showbase import ShowBase as showBase
from direct.gui.DirectGui import DirectButton

class Game(showBase.ShowBase):

    def __init__(self):
        showBase.ShowBase.__init__(self)

        self.accept("escape", base.userExit)
        self.accept("space", self.resize)

        self.btn = DirectButton(text = "Kitten",
                                text_align = TextNode.ALeft,
                                scale = 0.07)

    def resize(self):
        print ("Resizing")

        self.btn["frameSize"] = (-2, 2, -1, 1)
        self.btn["text_pos"] = (-2, 0)
        
app = Game()
app.run()
1 Like

Indeed, that turned out to work quite well after modifying your example a bit, so thank you very much for this :slight_smile: !

Instead of widget.guiItem.getTextNode(), I had to call widget.component("text0") to get a reference to the OnscreenText associated with the widget and get the text alignment from it. The biggest problem was getting right-alignment to work; for this I had to wrap the OnscreenText in a NodePath and call get_tight_bounds on it to calculate the new left and right bounds.

It also works for the popup marker of a DirectOptionMenu. While working on this, I encountered what is possibly a bug; trying to assign a position to menu["popupMarker_pos"] results in the following output (no crash, though):

Cannot configure initialisation option "pos" for DirectFrame

Luckily I can just call set_pos on the marker itself (which I retrieve with menu.component("popupMarker")).

So that’s some nice progress I think :slight_smile: .

Thanks again!

It’s my pleasure–I’m glad to have been of service! And indeed, it looks like you’ve made good progress on this! :slight_smile:

I suspect that this might be part of a more-general issue: If I’m not much mistaken, DirectGUI widgets don’t seem to keep their “underscore-keywords”–that is, those that access some sub-component via the “<component>_<keyword>” syntax.

I have a pull-request open for one specific instance of this: DirectOptionMenu’s “item_<keyword>” stylisations. But perhaps it’s not a bad idea to consider whether there’s a more-general solution to the problem.