TableListBox drag image stops working

Turns out none of the fixes really fixed it…still a problem.

This is still a problem, and this code causes my app to slow to a crawl:

[code]void updateAllTopLevelWindows ()
{
#if JUCE_WIN32
static bool inUpdate = false;

if (!inUpdate)
{
inUpdate = true;

int n = TopLevelWindow::getNumTopLevelWindows();
for (int i = 0; i < n; ++i)
{
  TopLevelWindow* w = TopLevelWindow::getTopLevelWindow (i);
  ComponentPeer* peer = w->getPeer ();
  if (peer)
    peer->performAnyPendingRepaintsNow ();
}

inUpdate = false;

}
#endif
}

void GuiWorker::handleAsyncUpdate()
{
process ();

// This tries to solve the problem where continual
// streams of work cause some painting not to occur.
updateAllTopLevelWindows ();
}[/code]

However, I have found some new information in MSDN. Jules does any of this sound helpful?

LockWindowUpate()
"The purpose of the LockWindowUpdate function is to permit drag/drop feedback to be drawn over a window without interference from the window itself…"
http://msdn.microsoft.com/en-us/library/dd145034(v=vs.85).aspx

WM_SETREDRAW
http://msdn.microsoft.com/en-us/library/dd145219(v=vs.85).aspx

Not really sure how I could use that to help with your problem?

I was thinking something along the lines that when the drag image component has repaint() called to invalidate it’s bounds, we temporarily supress repainting of all other components until the drag image component has completed a call to paint(), then we turn the repainting back on? Not quite sure how to do that cleanly

It all seems a bit convoluted, especially since this is something that you don’t seem to be able to reproduce very easily. If I was going to add it, I think I’d first want to see the problem myself, so would need to know exactly how to get it to happen…

Hmm…okay let me work on that

Also - it is easily reproduced. Within my app at least. Whenever I have a certain amount of pixels animated (components which call repaint() on themselves in response to another thread indicating that some state has changed), the problem appears. If I am animating just one or two components (for example, an output level meter and a scrolling waveform repainting over and over) there is no problem. But once I get two or more large components redrawing (for example, two scrolling waveforms) that’s when the drag image stops appearing.

Okay here you go:

[code]#include “juce.h”

int gTimestamp = 0;

struct Waveform : Component
{
unsigned long m_id;
Colour m_colour;
Waveform ()
{
m_id = reinterpret_cast (this);
Random rand (m_id);
m_colour = Colour::fromRGB (uint8(rand.nextInt (255)),
uint8(rand.nextInt (255)),
uint8(rand.nextInt (255)));
}
void paint (Graphics& g)
{
const Rectangle bounds = getLocalBounds ();
g.setColour (Colours::black);
g.fillRect (bounds);
Random rand (m_id);
const int amount = gTimestamp % 4096;
for (int i = 0; i < amount; ++i)
rand.nextFloat ();
g.setColour (m_colour);
for (int x = bounds.getX() + 1; x < bounds.getRight() - 1; ++x)
{
const int h = int (rand.nextFloat() * ((bounds.getHeight()-2)/2));
const Rectangle r (x, bounds.getCentreY() - h, 1, 2*h);
g.fillRect ®;
}
}
};

struct DragSource : Component
{
void paint (Graphics& g)
{
const Rectangle bounds = getLocalBounds ();
g.setColour (Colours::red);
g.fillRect (bounds);
g.setColour (Colours::black);
g.drawRect (bounds, 1);
g.setFont (Font(16).boldened());
g.drawText (“DRAG”, bounds.getX(), bounds.getY(),
bounds.getWidth(), bounds.getHeight(), Justification::centred, false);
}

void mouseDown (const MouseEvent& e)
{
const Rectangle bounds = getLocalBounds ();

DragAndDropContainer* const dragContainer
    = DragAndDropContainer::findParentDragContainerFor (this);

if (dragContainer != 0)
{
  Image di (Image::RGB, bounds.getWidth(), bounds.getHeight(), false);
  const Rectangle <int> r = di.getBounds();
  {
    Graphics g (di);
    g.setColour (Colours::red);
    g.fillRect (r);
    g.setColour (Colours::black);
    g.drawRect (r, 1);
  }

  MouseEvent e2 (e.getEventRelativeTo (this));
  Point <int> p (-e.x, -e.y);
  dragContainer->startDragging ("Stuff", this, di, true, &p);
}

}
};

struct ContentComponent : Component, Button::Listener
{
int m_next_y;

ContentComponent()
: m_next_y (64)
{
setSize(1024, 768);

Component* c;
c = new DragSource;
c->setBounds (16, 16, 48, 32);
addAndMakeVisible (c);

Button* b = new TextButton ("ADD");
b->setBounds (96, 16, 64, 32);
addAndMakeVisible (b);
b->addListener (this);

}

~ContentComponent ()
{
deleteAllChildren ();
}

void paint (Graphics& g)
{
const Rectangle bounds = getLocalBounds ();
g.setColour (Colours::white);
g.fillRect (bounds);
}

void buttonClicked (Button*)
{
const int m = 8; // margin
const int w = 800; // width
const int h = 32; // height
const int x = 16;
const int y = m_next_y;

Component* c = new Waveform;
c->setBounds (x, y, w, h);
addAndMakeVisible (c);

m_next_y = y + h + m;

}
};

struct Worker : Thread
{
AsyncUpdater* m_updater;

explicit Worker (AsyncUpdater* updater)
: Thread (“Worker”)
, m_updater (updater)
{
}

void run ()
{
while (!threadShouldExit())
{
m_updater->triggerAsyncUpdate ();
sleep (10);
}
}
};

struct MainWindow : DocumentWindow, DragAndDropContainer, AsyncUpdater
{
Worker m_worker;

MainWindow ()
: DocumentWindow (JUCE_T(“Test”)
, Colours::black
, DocumentWindow::allButtons
, true )
, m_worker (this)
{
ContentComponent* p = new ContentComponent;
setResizable (true, false);
setContentOwned (p, true);
centreWithSize (getWidth(), getHeight());
setVisible( true );

m_worker.startThread ();

}

~MainWindow()
{
m_worker.stopThread (-1);
}

void handleAsyncUpdate ()
{
gTimestamp++;
repaint ();
}

void closeButtonPressed()
{
JUCEApplication::quit();
}
};

struct MainApp : JUCEApplication
{
MainApp() : mainWindow(0) { s_app=this; }
~MainApp() { s_app=0; }
static MainApp& GetInstance() { return *s_app; }
const String getApplicationName() { return JUCE_T(“JuceTest”); }
const String getApplicationVersion() { return JUCE_T(“0.1.0”); }
bool moreThanOneInstanceAllowed() { return true; }
void anotherInstanceStarted (const String& commandLine) {}

void initialise (const String& commandLine)
{
mainWindow = new MainWindow;
}

void shutdown()
{
delete mainWindow;
}

static MainApp* s_app;
MainWindow* mainWindow;
};

MainApp* MainApp::s_app = 0;

START_JUCE_APPLICATION (MainApp)
[/code]

Let me know if this compiles and runs for you.

The red “DRAG” box is a draggable component. If you build Release under Windows you should be able to drag it at first. When you press Add, you will get an additional waveform. With enough waveforms, and if the window is big enough (since the window repaints itself white) eventually the drag image will stop being drawn.

If you have a bunch of waveforms up and shrink the window horizontally to something suitably small the drag image will come back.

Whether or not the drag image appears depends on how much time the updates take. Debug builds will stop showing the drag image much sooner. And it depends on the power of the machine.

In case anyone was wondering this is the output of the demo application:

Thanks for the example, I’ve reproduced it - very odd indeed! I’ll see if I can figure something out.

Does it happen on other platforms besides Windows?

No, it’s certainly a Windows thing.

On a somewhat related note, there is no way to tell a ListBox (and correspondingly, a TableListBox) to use ‘false’ for the value of allowDraggingToOtherJuceWindows it passes to DragAndDropContainer::startDragging().

Fixed in the latest tip!

Now…how do we tell the ListBox that we don’t want to allow dragging to other windows?