Шаг 376 - Прилипание окон как в WinAmp

Для начала разберемся, что к чему прилипает.

Небольшое отступление как создать равноправные дочерние окна по типу немодального диалогового окна регистрируется класс дочернего окна со своей процедурой а затем:

hwndList[0] = CreateWindow(szChild, "Child 1", WS_OVERLAPPEDWINDOW |
WS_VISIBLE, W_USEDEFAULT, CW_USEDEFAULT,100, 50, hwnd, NULL, hInstance,
NULL) ;

В принципе все, что нужно это обработать WM_WINDOWPOSCHANGING и скорректировать, если необходимо положение окна те осуществить проверку на близость к конкретному объекту. И еще одно нужно знать что происходит с окном двигают ли его или изменяют размер ухватив за левую правую верхнюю нижнюю кромку эту инфу можно получить из

WM_MOVING, WM_SIZING :

Узнаем какую кромку тянем -

void DockedOnSizing(WPARAM wParam, int &fMoving)
{
	fMoving&=~15;
	if(wParam==9)
		fMoving|=15;
	else
	{
		if(wParam==1 || wParam==4 || wParam==7) fMoving|=1;
		if(wParam==2 || wParam==5 || wParam==8) fMoving|=2;
		if(wParam==3 || wParam==4 || wParam==5) fMoving|=4;
		if(wParam==6 || wParam==7 || wParam==8) fMoving|=8;
	}
}

Изменяем, все кромки, просто тянем окно:

void DockedOnMoving(WPARAM wParam, int &fMoving)
{
fMoving|=15;
}

Осуществляется проверка на близость к десктопу и соответственную корректировку положения

//lpwp - это lParam WM_WINDOWPOSCHANGING
//fMoving - это ранее полученное значение кромки
//StickAt - это контролируемое расстояние
void IsCloseToDeskTop(LPWINDOWPOS lpwp, int fMoving, int StickAt)
{
RECT WorkArea;
SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkArea, 0);
WorkArea.right -= lpwp->cx;
WorkArea.bottom -= lpwp->cy;
if( abs(WorkArea.left - lpwp->x) <= StickAt && fMoving&1)
{
if( !(fMoving&2) ) lpwp->cx +=lpwp->x - WorkArea.left;
lpwp->x = WorkArea.left;
}
if( abs(WorkArea.right - lpwp->x) <= StickAt && fMoving&2)
{
if( !(fMoving&1) ) lpwp->cx +=WorkArea.right - lpwp->x ;
else lpwp->x = WorkArea.right;
}
if( abs(WorkArea.top - lpwp->y) <= StickAt && fMoving&4)
{
if( !(fMoving&8) ) lpwp->cy += lpwp->y - WorkArea.top;
lpwp->y = WorkArea.top;
}
if( abs(WorkArea.bottom - lpwp->y) <= StickAt && fMoving&8)
{
if( !(fMoving&4) ) lpwp->cy += WorkArea.bottom - lpwp->y ;
else lpwp->y = WorkArea.bottom;
}
}

Анализ на близость двух окон

//hwndDockedTo - окно для анализа
//lpwp - это lParam WM_WINDOWPOSCHANGING
//fMoving - это ранее полученное значение кромки
//StickAt - это контролируемое расстояние
bool IsCloseToWindow(HWND hwndDockedTo, LPWINDOWPOS lpwp, int fMoving, int
StickAt)
{ 
bool DockedByX=false;
bool DockedByY=false;
RECT WorkArea;

if(hwndDockedTo)	// если окно непустое
{
// анализ положения двух окон
GetWindowRect(hwndDockedTo, &WorkArea);
if( lpwp->y + lpwp->cy > WorkArea.top - StickAt && lpwp->y < WorkArea.bottom + StickAt)
{
if( fMoving&2 && abs(WorkArea.left - lpwp->cx - lpwp->x) <= StickAt)
{
if( !(fMoving&1) ) lpwp->cx = WorkArea.left-lpwp->x;
else lpwp->x = WorkArea.left-lpwp->cx;
DockedByY=true;
}
if( fMoving&1 && abs(WorkArea.right - lpwp->x) <= StickAt)
{
if( !(fMoving&2) ) lpwp->cx = lpwp->x - WorkArea.right + lpwp->cx;
lpwp->x = WorkArea.right;
DockedByY=true;
}
if(DockedByY)
{
if( WorkArea.bottom > lpwp->y + lpwp->cy/2)
if( fMoving&4 && abs(WorkArea.bottom - lpwp->cy - lpwp->y) <= StickAt) lpwp->y = WorkArea.bottom - lpwp->cy;else;
else
if( fMoving&4 && abs(WorkArea.bottom - lpwp->y) <= StickAt) lpwp->y = WorkArea.bottom;
if(WorkArea.top < lpwp->y + lpwp->cy/2)
if( fMoving&8 && abs(WorkArea.top - lpwp->y) <= StickAt) lpwp->y = WorkArea.top;else;
else
if( fMoving&8 && abs(WorkArea.top - lpwp->y - lpwp->cy) <= StickAt) lpwp->y = WorkArea.top-lpwp->cy;
}
}

if( lpwp->x + lpwp->cx > WorkArea.left && lpwp->x < WorkArea.right)
{
if( fMoving&8 && abs(WorkArea.top - lpwp->y - lpwp->cy) <= StickAt)
{
if( !
(fMoving&4) ) lpwp->cy = WorkArea.top - lpwp->y;
else lpwp->y = WorkArea.top - lpwp->cy;
DockedByX=true;
}
if( fMoving&4 && abs(WorkArea.bottom - lpwp->y ) <= StickAt)
{
if(!(fMoving&8)) lpwp->cy = lpwp->y - WorkArea.bottom + lpwp->cy;
lpwp->y = WorkArea.bottom;
DockedByX=true;
}
if(DockedByX)
{
if( fMoving&1 && abs(WorkArea.left - lpwp->x) <= StickAt) lpwp->x =
WorkArea.left;
if( fMoving&2 && abs(WorkArea.right - lpwp->x-lpwp->cx) <= StickAt)
lpwp->x = WorkArea.right-lpwp->cx;
}
}
}
return (DockedByX || DockedByY);
}

Комментировать все вычисления можно заморится основная идея взяли два прямоугольника и рассмотрели все варианты их взаимоположения и движения разных кромок Одна рекомендация StickAt делать не больше чем мин ширина окна/2 или мин высота/2. Теперь как использовать все это добро, в каждую оконную процедуру чье окно должно быть липнущим добавить

1 static int fSave; флажок активной кромки.

2

case WM_MOVING :
DockedOnMoving(wParam, fSave);
return true;

case WM_SIZING :
DockedOnSizing(wParam, fSave);
return true;

case WM_WINDOWPOSCHANGING :
IsCloseToDeskTop((LPWINDOWPOS)lParam, fSave, 15);
IsCloseToWindow(hwnd1, (LPWINDOWPOS)lParam, fSave, 15) ;
...// список окон подверженных тестироватию
IsCloseToWindow(hwndn, (LPWINDOWPOS)lParam, fSave, 15) ;
return 0;

Вроде все работает :). Пример в проекте.

Шаг прислал Куроедов Д. В.


Загрузить проект | Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем.