DirectScrollBar buttons shimmy and disappear

I made a DirectScrollBar with custom thumb and inc/dec buttons. Instead of just supplying a single image for each button, I used the egg-texture-cards program to create an egg file from 4 different raw images so that the buttons would have a different appearance for hover and click.

The raw images are all 16x16 png files. Here is the normal (non-hover, non-clicked) image for the thumb:

It has a 1 pixel white border which is hard to see against this white page background.

I call egg-texture-cards like this:

egg-texture-cards -o scrollbar_left_button.egg -g '-8,8,-8,8' scrollbar_left_normal.png scrollbar_left_clicked.png scrollbar_left_rollover.png scrollbar_left_disabled.png
egg-texture-cards -o scrollbar_right_button.egg -g '-8,8,-8,8' scrollbar_right_normal.png scrollbar_right_clicked.png scrollbar_right_rollover.png scrollbar_right_disabled.png
egg-texture-cards -o scrollbar_thumb_button.egg -g '-8,8,-8,8' scrollbar_thumb_normal.png scrollbar_thumb_clicked.png scrollbar_thumb_rollover.png scrollbar_thumb_disabled.png

Then, I use this code to create a scroll bar:

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject
from direct.gui.DirectGui import DirectScrollBar
from direct.gui.DirectGuiGlobals import VERTICAL
from direct.gui.DirectGuiGlobals import HORIZONTAL

class World(DirectObject):
	def __init__(self):

		self.accept( 'window-event', self.windowEventHandler) 
        	self.button_size = 16
        	self.half_button_size = self.button_size / 2
        	self.button_padding = self.button_size / 4
        	self.button_radius = self.half_button_size + self.button_padding
        	self.three_button_radii = 3 * self.button_radius
        
        	self.scrollbar_indent = 4 * self.button_radius + self.button_padding
        	self.scrollbar_width = 10
        	self.half_scrollbar_width = self.scrollbar_width / 2
        
        	self.slider_indent = 2 * self.button_radius + self.button_padding
        

		scrollbar_thumb_maps = loader.loadModel("scrollbar_thumb_button")
		self.scrollbar_thumb_geom = (scrollbar_thumb_maps.find('**/scrollbar_thumb_normal'), 
                                        scrollbar_thumb_maps.find('**/scrollbar_thumb_clicked'), 
                                        scrollbar_thumb_maps.find('**/scrollbar_thumb_rollover'), 
                                        scrollbar_thumb_maps.find('**/scrollbar_thumb_disabled'))

       		scrollbar_left_maps = loader.loadModel("scrollbar_left_button")
       		self.scrollbar_left_button_geom = (scrollbar_left_maps.find('**/scrollbar_left_normal'), 
                                              scrollbar_left_maps.find('**/scrollbar_left_clicked'), 
                                              scrollbar_left_maps.find('**/scrollbar_left_rollover'), 
                                              scrollbar_left_maps.find('**/scrollbar_left_disabled'))
                                                        
        	scrollbar_right_maps = loader.loadModel("scrollbar_right_button")
        	self.scrollbar_right_button_geom = (scrollbar_right_maps.find('**/scrollbar_right_normal'), 
                                                scrollbar_right_maps.find('**/scrollbar_right_clicked'), 
                                                scrollbar_right_maps.find('**/scrollbar_right_rollover'), 
                                                scrollbar_right_maps.find('**/scrollbar_right_disabled'))
        
        	self.scrollbar = DirectScrollBar(value = 0.5, command = self.handle_scrollbar, 
                                            manageButtons = True, 
                                            scrollSize = .001, 
                                            pageSize = .1, 
                                            
                                            thumb_relief = None, 
                                            decButton_relief = None, 
                                            incButton_relief = None, 
                                            
                                            thumb_pressEffect = 1, 
                                            
                                            thumb_geom=self.scrollbar_thumb_geom, 
                                            decButton_geom=self.scrollbar_left_button_geom, 
                                            incButton_geom=self.scrollbar_right_button_geom, 
                                            resizeThumb=False)

        	self.scrollbar.reparentTo(pixel2d)

	def handle_scrollbar(self):
		print self.scrollbar['value']
	

	def windowEventHandler( self, window=None ):
		if window is not None: # window is none if panda3d is not started
			wp = window.getProperties()
		        wpXSize = wp.getXSize()
            		wpYSize = wp.getYSize()

            		self.scrollbar['frameSize'] = (-.5*wpXSize + self.scrollbar_indent, .5*wpXSize - self.scrollbar_indent, -self.half_scrollbar_width, self.half_scrollbar_width)
            		self.scrollbar.setPos(.5*wpXSize, 0, -self.button_radius)


w = World()
run()

The untouched window then looks like this:

Here is an enlarged view of the thumb:

After clicking on the right arrow button (the increment button, at the right end of the scroll bar), the window looks like this:

Again, here is an enlarged view of the thumb:

See how the left edge of the thumb appears dim or clipped?

After clicking on the right arrow button for a second time, the window looks like this:

The enlarged view of the thumb now looks like this:

See how now the right edge is clipped, or dim.

But after clicking on the right arrow button for a third time, everything seems back to normal. Here is the window after 3 clicks:

The enlarged thumb looks like this now:

It seems back to normal.

That is one problem. The second problem is that after a horizontal resize, my inc/dec buttons just disappear:

I should mention that dragging the thumb along the scrollbar does not seem to leave the thumb in a state that looks clipped, IF it was not in such a state to begin with. However, if it was clipped at the start of the drag, it will look clipped at the end of the drag.

I’m guessing that clicking on the inc/dec buttons causes the thumb to translate by the amount specified by the “scrollSize” parameter to DirectScrollBar, and this translation distance does not match up with pixel widths, causing the thumb to end up “between” pixels. After a few clicks, maybe the fractional pixel translation amounts add up to a whole pixel, and the thumb is aligned again.

If this is the case, is there anything I could do in my python code to avoid these artefacts?

Thank you.

Your guess seems accurate. So, there are one of two solutions: (1) you could compute the appropriate scrollSize and pageSize parameters to be exactly an integer number of pixels. The user could also move the thumb by dragging it directly, but presumably this will always be an integer number of pixels by its nature.

(2) you could try using a larger texture, for instance 32x32 or even 64x64, with mipmapping enabled, so that it would have a better chance of being antialiased correctly when it is drawn on an off-pixel boundary. I don’t know how successful this would be, but it might be worth a try.

David

Thank you very much; I tried option 1, and it does help, but it seems to just increase the wavelength of the shimmy, because I guess there will always be some floating point round-off. I will play with it some more.

I’m still curious about my second problem; why do the scroll buttons disappear when I resize my window horizontally?

Ah, that appears to be a bug in DirectScrollBar (actually within the PGSliderBar implementation of DirectScrollBar). The bug manifests when you resize a scroll bar after setting an explicit geom for the inc and dec buttons.

My apologies for the bug. I’ve just committed a fix, which will be picked up by 1.7.1 (still unscheduled), or by the next snapshot build.

David

Thank you very much for your attention. Bummer on that bug.

One workaround is to set manageButtons = False and then position the buttons yourself in windowEventHandler().

David

Yes, I was thinking about that, but would I not in that case have to handle the dragging of the thumb myself also? And the rollover effects as well?

No. Those effects are handled by different systems. The only effect of manageButtons = True is the automatic placement of the buttons at each end of the scroll bar, which is what is failing in this case.

David

Thank you; I’m stupid, I just tried it after I posted and saw that the drag was still working. Thank you! This will work for me!