Updating multiple dialog windows in the background


#1

I have a modeless dialog window that can have several instances of it appearing within the app. It inherits the timer class which updates the window every few hundred milliseconds. I run the app, pop open the dialog, click on the app window to bring the focus back to it, dialog updates normally in the background with repaints, and everything works as expected.

When I pop open a second instance of the dialog, the first dialog stops updating, but the second dialog updates normally. If I click on and bring the focus back to the first dialog, it then begins updating normally, but the second dialog stops updating. When I click back on the app window to bring the focus back to it, the dialog that last had the focus updates normally, but the other one doesn’t.

Anyone have any clues as to what I might be doing wrong? I’ve dug through the JUCE source, but can’t see what would block the repaints.

  • kbj

#2

There’s nothing that’d stop repaints - maybe your code’s starting or stopping the timer when it gets/loses keyboard focus?


#3

This is a very simplified version of what I did:

class Dial : public Slider, public Timer
{
public:
	Dial(double dialTicks=100.0f) : Slider (T("Dial"))
	{
		setSliderStyle (Slider::Rotary);
		setRotaryParameters (float_Pi * 0.0f, float_Pi * 2.0f, false);
		setTextBoxStyle (Slider::NoTextBox, true, 0, 0);
		setRange (1.0f, dialTicks+1.0f);
		setValue(1.0f);
	}

	~Dial()
	{
		stopTimer();
	}

	void timerCallback ()
	{
		double val = getValue () + 1.0f;
		if (val > getMaximum ())
			val = getMinimum ();
		setValue(val);
	}
};

class MyDialog : public DialogWindow
{
public:
	MyDialog () : DialogWindow ( String(T("Dial")), RCColours::lightgrey )
	{
		Dial* d = new Dial ();
		addAndMakeVisible (d);
		d->setBounds(50, 50, 500, 500);

		d->startTimer(100);
	}

	~MyDialog ()
	{
		deleteAllChildren ();
	}

	void closeButtonPressed()
	{
		setVisible (false);
	}

};

void SubClassedLookAndFeel::drawRotarySlider (Graphics& g,
                                    int x, int y,
                                    int width, int height,
                                    float sliderPos,
                                    const float rotaryStartAngle,
                                    const float rotaryEndAngle,
                                    Slider& slider)
{
	const float radius = jmin (width / 2, height / 2) - jmax(m_thumb->getWidth()/2, m_thumb->getHeight()/2);
	const float centreX = x + width * 0.5f;
	const float centreY = y + height * 0.5f;
	const float rx = centreX - radius;
	const float ry = centreY - radius;
	const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);

	double x1 = sin(angle)*1.0f;
	double y1 = -cos(angle)*1.0f;
	g.drawImageAt(m_thumb, x1 * radius + centreX - thumb->getWidth()/2, y1 * radius + centreY - thumb->getHeight()/2);
}

I’ve subclassed LookAndFeel::drawRotarySlider to do nothing but draw a dot at the current position. If the rotary slider size is small, it updates okay. If it’s the size in the code above, no updates, even though there isn’t an increase in the scale of the actual graphics (same small dot, just a placement further away from the center).

  • kbj

#4

hmm - what’s that “thumb” variable you use in the drawImageAt line? Don’t you mean “m_thumb”?


#5

Whoops! Yeah, it should be m_thumb. That’ll teach me to cut and paste from different sources.

Regardless, even with the regular Slider class, a big rotary slider will not update under a timer. The same problem exists with your MIDI keyboard class – if the component is too large, nothing will update in the background under a timer in any window other than the one that last had the focus.

  • kbj

#6

I tried the code you sent and it worked just fine for me. Maybe send me a complete project that doesn’t work and I’ll have a look?


#7

Maybe send me a complete project that doesn’t work and I’ll have a look?

Urk! Don’t think I can do that, but I can make a bare bones example to send you that will exhibit the problem.

-kbj


#8

Yes, anything that’s sure to show problem would be good.


#9

Ok, for the record, the problem here turned out to be that because the windows were each continuously redrawing a massive, drop-shadowed, semi-transparent shape (in debug mode), the cpu was getting maxed-out at 100%. And when this happens, Windows seems to panic and only perform repaints for the foreground window… all the underlying code was working just fine, but the repaint messages were just never getting delivered.

Having had a think about this, I’d say the best solution is not to do such an insane amount of drawing!


#10

The example I sent you was just using the regular Slider class. In the actual code (as shown above), I subclass LookAndFeel::drawRotarySlider to do nothing but draw a dot image at the current value location. When I make the slider size small, all is okay. When the slider size is large (doing nothing more than drawing the same dot at a larger radius), the repaints never happen.

If this is a problem in the JUCE library, I will just have to figuratively smash a hole the wall and use BitBlt directly and violate the class encapsulation, not something I like doing.

  • kbj

#11

It’s not a juce problem, except in the sense that the slider class you’re using has a drop-shadow effect that has to re-render a blur on the entire component every time it repaints. It’s not designed to be used any bigger than a normal slider size.

If you set it to use the ShinyLookAndFeel (which doesn’t use a shadow) or write a more efficient component, it’ll be fine. No need to do any hacking.


#12

Erm, perhaps I’m not being clear. I have a class called WindowsLookAndFeel (how original, I know) derived from LookAndFeel. In it I have a member called “drawRotarySlider” which does exactly what you see in my little example above.

I do this in MyJUCEApplication::initialise:

static WindowsLookAndFeel look;
LookAndFeel::setDefaultLookAndFeel (&look);

I then instantiate a derived slider with an inherited timer, make it rotary, make it big, set off the timer, it then calls the WindowsLookAndFeel::drawRotarySlider member function to render the slider. There’s no drop shadows, no alpha blending, no paths, just a straight blit of an image to a position on the screen. Are you saying that this takes up a lot of cpu horsepower? Perhaps the repaint function to clear the previously rendered graphic is using up a lot of cpu horsepower. Is this a possibility?

  • kbj

#13

Honestly, it’s got a drop-shadow! The shadow is set as a component effect, not drawn in the repaint routine! There was a shadow in the code you sent me, and it’s the shadow that uses up the cpu!


#14

Doh!! I completely missed LookAndFeel::getSliderEffect() – that’s what was slowing it down! I reimplemented it to return null and, tada, no more slowdown.

  • kbj

#15

Okay, an additional piece of information for those who might be trying something similiar and subclassing a component, like creating a new slider type or button, etc.:

Don’t set setBufferedToImage() true within the subclassed component. The buffer will be flushed every time the component is repainted and this will suck down a lot of cpu resources. Instead, in your application, set setBufferedToImage() true in the parent component that is the container for your custom component. Doing this cut my cpu usage from approximately 30% to 5% for my one large rotary slider.

. . . Jules, I’ve hacked the component class slightly and made repaintParent protected so I can override it to do nothing in my slider class. This cuts my cpu usage down to 2%. Am I going to run into trouble doing this?

  • kbj

#16

[quote=“killerbobjr”]
. . . Jules, I’ve hacked the component class slightly and made repaintParent protected so I can override it to do nothing in my slider class. This cuts my cpu usage down to 2%. Am I going to run into trouble doing this?

  • kbj[/quote]

mighty big trouble.

If you find yourself hacking things, you’re probably going about it all the wrong way. Why not just write a custom component that does the thing you want it to, instead of trying to force the slider to do a job it’s not meant for?

Then you could make your custom component opaque, and that’d minimise the amount of repainting that gets done, cutting its parent component out of the loop.


#17

Why not just write a custom component that does the thing you want it to

More work, very little time left. I’ll do things properly later. For now I’ve just reverted to the original component class and can live with the extra few percent cpu hit.

If you find yourself hacking things, you’re probably going about it all the wrong way.

Then I probably shouldn’t tell you about all the other hacks I did for quick n’ dirty fixes. :slight_smile:

-kbj


#18

i was just doing this kind of stuff in the beginning when used juce… but time after time, trial and error, you should end like me to not dirty any case the juce codebase (well, yes if is a bug)… just create a new class, derive from the one you need and make all the mistakes you want in there… this way you don’t compromise the work of any other piece of code… you tweaked one piece for making it behave like you want, but probably other 3 or 4 pieces of the logic gets fucked up… beware don’t mess up with the core directly until you know what you’re doing.
be cute, but don’t forget to listen to jules words !

lou


#19