Don't perform hit testing if we haven't painted the graphics window.

This change is quite subtle. The goal is to improve responsiveness
of highlighting even further. To understand this change you need
to keep in mind that Windows and Gtk have dramatically different
behavior for paint (WM_PAINT in Windows, expose in Gtk) and
mouse move events.

In Windows, WM_PAINT and WM_MOUSEMOVE, unless sent explicitly,
are synthesized: WM_MOUSEMOVE is delivered when there are no other
messages and the current cursor position doesn't match the remembered
one, and WM_PAINT is delivered when there are no other messages,
even WM_MOUSEMOVE. This is pretty clever because it doesn't swamp
programs that are slow to process either of those events with even
more of them, ensuring they remain responsive.

In Gtk, expose events are delivered at the end of the frame whenever
there is an invalid view, and every single mouse move that happened
will result in a separate event.

If mouse move events are handled quickly, then the behavior is
identical in either case:
  * process mouse move event
    * perform hit testing
    * invalidate view
  * no more events to process!
    * there are invalid views
      * repaint

If, however, mouse move events are handled slower, then the behavior
diverges. With Gtk:
  * process mouse move event
    * perform hit testing (slow)
      * while this happens, ten more mouse move events are added
    * invalidate view
  * end of frame!
    * there are invalid views
      * repaint
  * process mouse move event...
As a result, the Gtk-hosted UI hopelessly lags behind user input.
This is very irritating.

With Windows:
  * process mouse move event
    * perform hit testing (slow)
      * while this happens, mouse was moved
    * invalidate view
  * process mouse move event...
As a result, the Windows-hosted UI never repaints while the mouse
is moved. This is also very irritating.

Commit HEAD^ has fixed the problems with Gtk-based UI by making
hit testing so fast that mouse move events never quite overflow
the queue. There's still a barely noticeable lag but it's better.

However, the problems with Windows remained because while the queue
doesn't *overflow* with the faster hit testing code, it doesn't go
*empty* either! Thus we still don't repaint.

This commit builds on top of HEAD^ and makes it so that we don't
actually hit test anything if we haven't painted the result of
the previous hit test already. This fixes the problem on Windows
but also helps Gtk a little bit.

Curiously, the Cocoa-based UI never suffered from any of these
problems. To my understanding (it's somewhat underdocumented), it
processes mouse moves like Windows, but paints like Gtk.
pull/4/head
whitequark 2016-03-06 10:55:02 +00:00
parent bda2835e9f
commit 6e56b00b9a
2 changed files with 11 additions and 3 deletions

View File

@ -383,7 +383,7 @@ void GraphicsWindow::HitTestMakeSelection(Point2d mp) {
if(!s.Equals(&hover)) { if(!s.Equals(&hover)) {
hover = s; hover = s;
InvalidateGraphics(); PaintGraphics();
} }
} }

View File

@ -238,7 +238,14 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
// a point, mouse down (thus selecting it), and drag, in an // a point, mouse down (thus selecting it), and drag, in an
// effort to drag the point, but instead hover a different // effort to drag the point, but instead hover a different
// entity before we move far enough to start the drag. // entity before we move far enough to start the drag.
if(!leftDown) HitTestMakeSelection(mp); if(!leftDown) {
// Hit testing can potentially take a lot of time.
// If we haven't painted since last time we highlighted
// something, don't hit test again, since this just causes
// a lag.
if(!havePainted) return;
HitTestMakeSelection(mp);
}
} }
return; return;
} }
@ -261,6 +268,8 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
} }
return; return;
} }
havePainted = false;
switch(pending.operation) { switch(pending.operation) {
case DRAGGING_CONSTRAINT: { case DRAGGING_CONSTRAINT: {
Constraint *c = SK.constraint.FindById(pending.constraint); Constraint *c = SK.constraint.FindById(pending.constraint);
@ -449,7 +458,6 @@ void GraphicsWindow::MouseMoved(double x, double y, bool leftDown,
{ {
SS.GenerateAll(); SS.GenerateAll();
} }
havePainted = false;
} }
void GraphicsWindow::ClearPending(void) { void GraphicsWindow::ClearPending(void) {