DirectGUI by keyboard or gamepad: highlighting

I recently started a thread in which we ended up discussing the possibility of adding support for navigating DirectGUI via keyboard or gamepad. Built-in support seems likely to be some time away, so, for my current main project, I want to look at implementing such support on my end.

I have ideas for the actual navigation–how to handle moving between UI-elements. What I’m rather uncertain about is the question of how to highlight the currently-selected item.

The ideal, I would imagine, would be to have DirectGUI elements highlight as though the mouse were hovering over them–that would at least be consistent with mouse-interaction. But, as I mentioned in the thread linked-to above, DirectGUI (or DirectButton, at least) seems to be implemented in a way that runs counter to that.

Which is where I find myself uncertain.

I do have two potential expedients in mind:

First, I could add an indicator-image of some sort that shows up when a non-mouse input-device is used, and vanishes when the mouse is moved. This would be placed near the selected UI-element to convey that selection.

Second, I could (perhaps–I’m not sure that it would work) just warp the mouse to the selected UI-element, hoping that DirectGUI would treat that as a mouse-over.

Thoughts?

Mouse cursor is nothing to do with, as far as I know, it is uncontrollable. However, it is possible to emulate a cursor — a collision ray for the gui elements. Which will be handled by an X, Y gesture.

You can warp the mouse-cursor, I do believe. (It’s part of how I usually implement first-person controls, warping the cursor back to the centre of the screen.) Warping the mouse to the centre of the screen, for example, may be done like so:

if base.mouseWatcherNode.hasMouse and base.win.hasSize():
        base.win.movePointer(0, base.win.getXSize()/2, base.win.getYSize()/2)

(That uses Python 2.7 code; in Python 3 I would presumably want to use “//” instead of “/”.)

I suppose that I could have the gamepad thumbstick move an emulated cursor–but I’m not sure that doing so answers the question of how to make a given UI-control highlighted.

Hmm, I need to check, I remember trying to correct jerking to the side when exiting the menu. Then they added examples to the folder with the pandas, but did not use them. Thanks for the example.

I suggested using the collision node name of the gui elements with the ray. Next, set the focus and simply draw a frame, calculated using the bounding rectangle.

I checked the theory, not everything is so simple. What the idea should work is not working. :frowning:

from direct.showbase.ShowBase import ShowBase
from direct.gui.DirectButton import DirectButton

class MyApp(ShowBase):

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

        self.text1 = DirectButton(text = ("OK", "click!", "rolling over", "disabled"), scale=.1, pos = (0, 0, 0.8))
        self.text2 = DirectButton(text = ("OK", "click!", "rolling over", "disabled"),scale=.1, pos = (0, 0, 0.6))
        self.text3 = DirectButton(text = ("OK", "click!", "rolling over", "disabled"),scale=.1, pos = (0, 0, 0.4))
        self.text4 = DirectButton(text = ("OK", "click!", "rolling over", "disabled"),scale=.1, pos = (0, 0, 0.2))
        self.text5 = DirectButton(text = ("OK", "click!", "rolling over", "disabled"),scale=.1, pos = (0, 0, 0))
        
        self.accept('space', self.joystick)
        self.accept('mouse1', self.OnSpam)

    def OnSpam(self):
    
        print ("click")
        
    def joystick(self):
        messenger.send('mouse1')
        

app = MyApp()
app.run()

According to the theory, a click should occur if you hover the cursor and press the spacebar.

Yeah, I think that DirectGUI is implemented under the assumption that it will be operated by mouse. :/

It’s possible to have a selected GUI-item (maintained in your own code), and to call its command-function or give it focus, I believe. It’s the highlighting that’s the tricky part… :/

[edit]
That is to say, you can do this:

# Under some condition:
self.currentMainMenuControl = self.startGameButton

# When a button is pressed:
self.currentMainMenuControl.commandFunc(None)

[/edit]

That, in my experience, is a little trickier than you might expect.

I actually dealt with much the same in a side-project of mine recently: the view would start off looking at the sky because the “start game” button happened to be above the centre-point. I ended up not only resetting the mouse-position, but waiting a few frames before doing so, if I’m not much mistaken.

The most interesting is that the GUI is controlled by a separate mouse class. It was rather done to separate user requests and GUI.

Huh. I hadn’t noticed that! That’s an interesting decision on their part.