[BUG_FIXED] (Author: jgr [SF user name: joerngr]) Fix dragging undocked window performance issue while using Vista Aero UI style.

git-svn-id: svn://svn.tuxfamily.org/svnroot/notepadplus/repository/trunk@658 f5eea248-9336-0410-98b8-ebc06183d4e3
This commit is contained in:
Don Ho 2010-08-30 01:12:52 +00:00
parent 3fa8a89e76
commit 2c05fa7f37
2 changed files with 167 additions and 53 deletions

View File

@ -14,6 +14,10 @@
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Changed something around drawRectangle() (for details see there) to enhance
// speed and consistency of the drag-rectangle - August 2010, Joern Gruel (jg)
#include "precompiledHeaders.h"
#include "Gripper.h"
@ -77,7 +81,6 @@ Gripper::Gripper()
_hInst = NULL;
_hParent = NULL;
_hSelf = NULL;
_pDockMgr = NULL;
_pCont = NULL;
@ -97,7 +100,7 @@ Gripper::Gripper()
_hbm = NULL;
_hbrush = NULL;
memset(&_rcPrev, 0, sizeof(RECT));
memset(&_rcItem, 0, sizeof(RECT));
memset(&_tcItem, 0, sizeof(TCITEM));
memset(&_dockData, 0, sizeof(tDockMgr));
@ -211,7 +214,7 @@ LRESULT Gripper::runProc(UINT message, WPARAM wParam, LPARAM lParam)
getMousePoints(&pt, &ptBuf);
/* erase last drawn rectangle */
drawRectangle(ptBuf);
drawRectangle(NULL);
/* end hooking */
::UnhookWindowsHookEx(hookMouse);
@ -298,18 +301,13 @@ void Gripper::onMove()
::GetCursorPos(&pt);
getMousePoints(&pt, &ptBuf);
/* On first time: Do not erase previous rect, because it dosn't exist */
if (_bPtOldValid == TRUE)
drawRectangle(ptBuf);
/* tab reordering only when tab was selected */
if (_startMovingFromTab == TRUE)
{
doTabReordering(pt);
}
drawRectangle(pt);
_bPtOldValid = TRUE;
drawRectangle(&pt);
}
@ -324,14 +322,14 @@ void Gripper::onButtonUp()
::GetCursorPos(&pt);
getMousePoints(&pt, &ptBuf);
/* do nothing, when old point is not valid */
// do nothing, when old point is not valid
if (_bPtOldValid == FALSE)
return;
/* erase last drawn rectangle */
drawRectangle(ptBuf);
// erase last drawn rectangle
drawRectangle(NULL);
/* look if current position is within dockable area */
// look if current position is within dockable area
DockingCont* pDockCont = contHitTest(pt);
if (pDockCont == NULL)
@ -523,14 +521,53 @@ void Gripper::doTabReordering(POINT pt)
::UpdateWindow(_hParent);
}
void Gripper::drawRectangle(POINT pt)
// Changed behaviour (jg): Now this function handles erasing of drag-rectangles and drawing of
// new ones within one drawing step to the desktop. This is against flickering, but also it is
// necessary for the Vista Aero style - because in this case the control is given so much to
// the graphics driver, that accesses (especially read accesses) to the desktop window become
// too expensive to access it more than absolutely necessary. Besides, usage of the function
// ::LockWindowUpdate() was added, because with often redrawn windows in the background we had
// inconsistencies while erasing our drag-rectangle (because it could already have been erased
// on some places).
//
// Parameter pPt==NULL says that only erasing is wanted and the drag-rectangle is no more needed,
// thatswhy this also leads to a call of ::LockWindowUpdate(NULL) to enable drawing by others again.
// The previously drawn rectangle is memoried within _rectPrev (and _bPtOldValid says if it already
// is valid - did not change this members name because didn't want change too much at once).
//
// I was too lazy to always draw four rectangles for the four edges of the drag-rectangle - it seems
// that drawing an outer rectangle first and then erasing the inner stuff by drawing a second,
// smaller rectangle inside seems to be not slower - wich comes not unawaited, because it is mostly
// hardware-driven and each single draw has its own fixed costs.
//
// For further solutions I think we should leave this classic way of dragging and better use
// alpha-blending and always move the whole content of the toolbars - so we could leave the
// ::LockWindowUpdate() behind us.
//
// Besides, while debugging into the dragging process please let the ::LockWindowUpdate() out,
// by #undef the USE_LOCKWINDOWUPDATE in gripper.h, because it works for your debugging window
// as well, of course. Or just try by this #define what difference it makes.
//
void Gripper::drawRectangle(const POINT* pPt)
{
HANDLE hbrushOrig = NULL;
HBRUSH hbrushOrig= NULL;
HBITMAP hbmOrig = NULL;
RECT rc = {0};
RECT rcNew = {0};
RECT rcOld = _rcPrev;
// Get a screen device context with backstage redrawing disabled - to have a consistently
// and stable drawn rectangle while floating - keep in mind, that we must ensure, that
// finally ::LockWindowUpdate(NULL) will be called, to enable drawing for others again.
if (!_hdc)
_hdc = ::GetDC(NULL);
{
HWND hWnd= ::GetDesktopWindow();
#if defined (USE_LOCKWINDOWUPDATE)
_hdc= ::GetDCEx(hWnd, NULL, ::LockWindowUpdate(hWnd) ? DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE : DCX_WINDOW|DCX_CACHE);
#else
_hdc= ::GetDCEx(hWnd, NULL, DCX_WINDOW|DCX_CACHE);
#endif
}
// Create a brush with the appropriate bitmap pattern to draw our drag rectangle
if (!_hbm)
@ -538,25 +575,90 @@ void Gripper::drawRectangle(POINT pt)
if (!_hbrush)
_hbrush = ::CreatePatternBrush(_hbm);
if (pPt != NULL)
{
// Determine whether to draw a solid drag rectangle or checkered
getMovingRect(pt, &rc);
// ???(jg) solid or checked ??? - must have been an old comment, I didn't
// find here this difference, but at least it's a question of drag-rects size
//
getMovingRect(*pPt, &rcNew);
_rcPrev= rcNew; // save the new drawn rcNew
::SetBrushOrgEx(_hdc, rc.left, rc.top, 0);
hbrushOrig = ::SelectObject(_hdc, _hbrush);
// note that from here for handling purposes the right and bottom values of the rects
// contain width and height - its handsome, but i find it dangerous, but didn't want to
// change that already this time.
// line: left
::PatBlt(_hdc, rc.left, rc.top, 3, rc.bottom - 3, PATINVERT);
// line: top
::PatBlt(_hdc, rc.left + 3, rc.top, rc.right - 3, 3, PATINVERT);
// line: right
::PatBlt(_hdc, rc.left + rc.right - 3, rc.top + 3, 3, rc.bottom - 3, PATINVERT);
// line: bottom
::PatBlt(_hdc, rc.left, rc.top + rc.bottom - 3, rc.right - 3, 3, PATINVERT);
if (_bPtOldValid)
{
// okay, there already a drag-rect has been drawn - and its position
// had been saved within the rectangle _rectPrev, wich already had been
// copied into rcOld in the beginning, and a new drag position
// is available, too.
// If now rcOld and rcNew are the same, just stop further handling to not
// draw the same drag-rectangle twice (this really happens, it should be
// better avoided anywhere earlier)
//
if (rcOld.left==rcNew.left && rcOld.right==rcNew.right && rcOld.top== rcNew.top && rcOld.bottom==rcNew.bottom)
return;
// destroy resources
::SelectObject(_hdc, hbrushOrig);
rc.left = min(rcOld.left, rcNew.left);
rc.top = min(rcOld.top, rcNew.top);
rc.right = max(rcOld.left + rcOld.right, rcNew.left + rcNew.right);
rc.bottom = max(rcOld.top + rcOld.bottom, rcNew.top + rcNew.bottom);
rc.right -= rc.left;
rc.bottom-= rc.top;
}
else rc= rcNew; // only new rect will be drawn
}
else rc= rcOld; // only old rect will be drawn - to erase it
// now rc contains the rectangle wich encloses all needed, new and/or previous rectangle
// because in the following we drive within a memory device context wich is limited to rc,
// we have to localize rcNew and rcOld within rc...
//
rcOld.left= rcOld.left - rc.left;
rcOld.top = rcOld.top - rc.top;
rcNew.left= rcNew.left - rc.left;
rcNew.top = rcNew.top - rc.top;
HDC hdcMem= ::CreateCompatibleDC(_hdc);
HBITMAP hBm= ::CreateCompatibleBitmap(_hdc, rc.right, rc.bottom);
hbrushOrig= (HBRUSH)::SelectObject(hdcMem, hBm);
::SetBrushOrgEx(hdcMem, rc.left%8, rc.top%8, 0);
hbmOrig= (HBITMAP)::SelectObject(hdcMem, _hbrush);
::BitBlt(hdcMem, 0, 0, rc.right, rc.bottom, _hdc, rc.left, rc.top, SRCCOPY);
if (_bPtOldValid)
{ // erase the old drag-rectangle
::PatBlt(hdcMem, rcOld.left , rcOld.top , rcOld.right , rcOld.bottom , PATINVERT);
::PatBlt(hdcMem, rcOld.left+3, rcOld.top+3, rcOld.right-6, rcOld.bottom-6, PATINVERT);
}
if (pPt != NULL)
{ // draw the new drag-rectangle
::PatBlt(hdcMem, rcNew.left , rcNew.top , rcNew.right , rcNew.bottom , PATINVERT);
::PatBlt(hdcMem, rcNew.left+3, rcNew.top+3, rcNew.right-6, rcNew.bottom-6, PATINVERT);
}
::BitBlt(_hdc, rc.left, rc.top, rc.right, rc.bottom, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbrushOrig);
SelectObject(hdcMem, hbmOrig);
DeleteObject(hBm);
DeleteDC(hdcMem);
if (pPt == NULL)
{
#if defined(USE_LOCKWINDOWUPDATE)
::LockWindowUpdate(NULL);
#endif
_bPtOldValid= FALSE;
if (_hdc)
{
::ReleaseDC(0, _hdc);
_hdc= NULL;
}
}
else _bPtOldValid= TRUE;
}

View File

@ -29,6 +29,10 @@
class DockingCont;
class DockingManager;
// For the following #define see the comments at drawRectangle() definition. (jg)
#define USE_LOCKWINDOWUPDATE
//#undef USE_LOCKWINDOWUPDATE
// Used by getRectAndStyle() to draw the drag rectangle
static const WORD DotPattern[] =
@ -54,6 +58,11 @@ public:
~Gripper() {
if (_hdc) {
// usually this should already have been done by a call to drawRectangle(),
// here just for cases where usual handling was interrupted (jg)
#ifdef USE_LOCKWINDOWUPDATE
::LockWindowUpdate(NULL);
#endif
::ReleaseDC(0, _hdc);
}
if (_hbm) {
@ -75,7 +84,7 @@ protected :
void onButtonUp();
void doTabReordering(POINT pt);
void drawRectangle(POINT pt);
void drawRectangle(const POINT* pPt);
void getMousePoints(POINT* pt, POINT* ptPrev);
void getMovingRect(POINT pt, RECT *rc);
DockingCont * contHitTest(POINT pt);
@ -120,6 +129,9 @@ private:
POINT _ptOld;
BOOL _bPtOldValid;
// remember last drawn rectangle (jg)
RECT _rcPrev;
// for sorting tabs
HWND _hTab;
HWND _hTabSource;