Задача: "Липкие" окна
Исходник: прилипающие winamp окна, язык: C++ [code #554, hits: 7565]
автор: - [добавлен: 02.03.2008]
  1. // snap_helper.h >>
  2. #pragma once
  3. #ifndef __file__SNAP_HELPER_H
  4. #define __file__SNAP_HELPER_H
  5.  
  6. #include <vector>
  7. #include <algorithm>
  8. #include <functional>
  9. #include "windowsx.h"
  10. #include "windows.h"
  11.  
  12. class snap_helper
  13. {
  14. HWND m_wnd;
  15.  
  16. struct wnd_data
  17. {
  18. HWND m_wnd;
  19. bool m_is_neighbour;
  20.  
  21. wnd_data() : m_wnd(0), m_is_neighbour(false) {}
  22. wnd_data( HWND w ) : m_wnd(w), m_is_neighbour(false) {}
  23. wnd_data( HWND w, bool neighbour ) : m_wnd(w), m_is_neighbour(neighbour) {}
  24. };
  25.  
  26. //typedef std::list<wnd_data> container_wnd;
  27. typedef std::vector<wnd_data> container_wnd;
  28.  
  29. static container_wnd s_wnd_list;
  30. static HWND s_master_wnd;
  31. static int s_thresold;
  32.  
  33. enum DRAG_ANCHOR_X { DAX_UNKNOWN, DAX_LEFT, DAX_RIGHT, };
  34. enum DRAG_ANCHOR_Y { DAY_UNKNOWN, DAY_TOP, DAY_BOTTOM, };
  35.  
  36. DRAG_ANCHOR_X m_drag_anchor_x;
  37. DRAG_ANCHOR_Y m_drag_anchor_y;
  38.  
  39. int m_drag_err_x;
  40. int m_drag_err_y;
  41.  
  42. class is_eq_wnd : public std::unary_function<wnd_data, bool>
  43. {
  44. const HWND match;
  45.  
  46. public:
  47. is_eq_wnd( HWND m ) : match(m) {}
  48. bool operator() ( wnd_data& val ) { return val.m_wnd == match; }
  49. };
  50.  
  51. static void remove_window( HWND wnd )
  52. {
  53. container_wnd::iterator i = remove_if( s_wnd_list.begin(), s_wnd_list.end(), is_eq_wnd(wnd) );
  54. s_wnd_list.erase( i, s_wnd_list.end() );
  55. }
  56.  
  57. static void append_window( HWND wnd )
  58. {
  59. container_wnd::iterator i = find_if( s_wnd_list.begin(), s_wnd_list.end(), is_eq_wnd(wnd) );
  60. if( i == s_wnd_list.end() ) s_wnd_list.push_back( wnd );
  61. }
  62.  
  63. public:
  64.  
  65. static int thresold() { return s_thresold; }
  66. static void thresold( int t ) { s_thresold = t; }
  67.  
  68. static bool is_master( HWND wnd ) { return wnd == s_master_wnd; }
  69. bool is_master() const { return is_master( m_wnd ); }
  70.  
  71. void init( HWND wnd, bool is_master )
  72. {
  73. m_wnd = wnd;
  74.  
  75. append_window( wnd );
  76. if( is_master ) s_master_wnd = m_wnd;
  77. }
  78.  
  79. snap_helper( HWND wnd, bool is_master )
  80. {
  81. init( wnd, is_master );
  82. }
  83.  
  84. snap_helper()
  85. {
  86.  
  87. }
  88.  
  89. ~snap_helper()
  90. {
  91. if( s_master_wnd == m_wnd ) s_master_wnd = 0;
  92. remove_window( m_wnd );
  93. }
  94.  
  95.  
  96. private:
  97. //////////////////////////////////////////////////////////////////////////
  98. // helpers methods
  99. //////////////////////////////////////////////////////////////////////////
  100.  
  101. // расстояние достаточно близкое чтобы притянуться
  102. static bool is_close( int dx )
  103. {
  104. return dx > -s_thresold && dx < s_thresold;
  105. }
  106.  
  107. // точка pt внутри отрезка [x1, x2]?
  108. static bool pt_is_inside( int x1, int x2, int pt )
  109. {
  110. return pt >= x1 && pt <= x2;
  111. }
  112.  
  113. // отрезки [p1_x1, p1_x2], [p2_x1, p2_x2]
  114. // пересекаються или прилегают друг к другу?
  115. static bool is_intersect( int p1_x1, int p1_x2, int p2_x1, int p2_x2 );
  116.  
  117. // pt1 < pt2
  118. // Возвращает pt1 либо pt2 -- к чему ближе pt
  119. static int nearest_dest( int pt1, int pt2, int pt )
  120. {
  121. return abs(pt1-pt) < abs(pt2-pt) ? pt1-pt : pt2-pt;
  122. }
  123.  
  124. // вычисляет расстояния от прямоугольника окна (rc_wnd)
  125. // до другого прямоугольника (rc_snap), к которому исходное окно
  126. // может притянуться.
  127. // Здесь и далее:
  128. // dx1 -- расстояние от левого края окна до одной из граней прямоугольника
  129. // dx2 -- расстояние от правого края окна до одной из граней прямоугольника
  130. // dy1 -- расстояние от верхнего края окна ...
  131. // dy2 -- расстояние от нижнего края окна ...
  132. static void calc_dest( const RECT rc_wnd, const RECT rc_snap, int& dx1, int& dx2, int& dy1, int& dy2 );
  133.  
  134. // вычисляет минимальные расстояния от прямоугольника окна (rc_wnd)
  135. // до прямоугольника другого окна (rc_snap), к которому исходное окно
  136. // может притянуться.
  137. // Расстояния переписываются только если они больше исходных (dx1,dx2,dy1,dy2)
  138. static void calc_min_dest( const RECT rc_wnd, const RECT rc_snap, int& dx1, int& dx2, int& dy1, int& dy2 );
  139.  
  140. // вычисляет минимальное расстояние от прямоугольника (rc_base) окна (wnd)
  141. // до ограничевающего прямоугольника (rc_start -- обычно, это прямоугольник десктопа)
  142. // либо до прямоуольника одного из других окон из списка s_wnd_list
  143. static void find_nearest_dst( HWND wnd, const RECT& rc_base, const RECT& rc_start
  144. , int& dx1, int& dx2, int& dy1, int& dy2 );
  145.  
  146. // вычисляет минимальное расстояние от прямоугольника (rc_base) окна (wnd)
  147. // до ограничевающего прямоугольника (rc_start -- обычно, это прямоугольник десктопа)
  148. // либо до прямоуольника одного из других окон из списка s_wnd_list
  149. // В отличии от find_nearest_dst() применяется при поиске расстояний относительно
  150. // мастер-окна и, также, учитывает расстояния от окон соседствующих с ним.
  151. static void find_nearest_dst_master( HWND wnd, const RECT& rc_base, const RECT& rc_start
  152. , int& dx1, int& dx2, int& dy1, int& dy2
  153. , int nb_off_x, int nb_off_y );
  154.  
  155. // возвращает ограничевающий прямоугольник -- прямоугольник рабочего стола
  156. void find_nearest_destop_rect( const RECT& rc_wnd, RECT& rc_out );
  157.  
  158.  
  159. static void edge_to_anchor( int edge, DRAG_ANCHOR_X& anc_x, DRAG_ANCHOR_Y& anc_y );
  160. static LONG& rect_side_x( RECT& rc, DRAG_ANCHOR_X anc_x );
  161. static LONG& rect_side_y( RECT& rc, DRAG_ANCHOR_Y anc_y );
  162.  
  163. // Прямоугольники имеют соприкасающиеся грани?
  164. static bool is_neighbours( const RECT& rc1, const RECT& rc2 );
  165.  
  166. // Окна имеют соприкасающиеся грани?
  167. static bool is_neighbours( HWND wnd1, HWND wnd2 );
  168.  
  169. // отмечает в списке соседей данного окна.
  170. // Перед использованием нужно очистить свойство is_neighbour всех окон
  171. static void mark_neighbours( HWND master, container_wnd& wnd_list );
  172.  
  173. // отмечает в списке соседей данного окна.
  174. static void recalc_neighbours( HWND master, container_wnd& wnd_list );
  175.  
  176. //static void shift_window( HWND wnd, int dx, int dy );
  177. static void shift_neighbour_windows( HWND wnd, container_wnd& wnd_list, int dx, int dy );
  178. static void show_neighbour_windows( HWND wnd, container_wnd& wnd_list );
  179. static void show_all_windows( HWND wnd, container_wnd& wnd_list );
  180.  
  181.  
  182. public:
  183. //////////////////////////////////////////////////////////////////////////
  184. // event handlers
  185. //////////////////////////////////////////////////////////////////////////
  186.  
  187. void on_sizing( RECT* p_rc_new, int edge ); // WM_SIZING (return 1;)
  188. void on_moving( RECT* p_rc_new ); // WM_MOVING (return 1;)
  189. void on_enter_sizemove(); // WM_ENTERSIZEMOVE
  190. void on_activate(); // WM_NCACTIVATE
  191. };
  192.  
  193. #endif // __file__SNAP_HELPER_H
  194.  
  195. // snap_helper.cpp >>
  196. #include "StdAfx.h"
  197. #include "snap_helper.h"
  198.  
  199. snap_helper::container_wnd snap_helper::s_wnd_list;
  200. HWND snap_helper::s_master_wnd = 0;
  201. int snap_helper::s_thresold = 10;
  202.  
  203.  
  204. bool snap_helper::is_intersect( int p1_x1, int p1_x2, int p2_x1, int p2_x2 )
  205. {
  206. // return true; // любопытный эффект.
  207.  
  208. return (p2_x1 <= p1_x1 && p2_x2 >= p1_x2 ) ||
  209. //(p1_x1 <= p1_x2 && p1_x2 >= p2_x2 ) ||
  210. pt_is_inside( p1_x1, p1_x2, p2_x1 ) ||
  211. pt_is_inside( p1_x1, p1_x2, p2_x2 ) ||
  212. (p2_x2 + 1 == p1_x1) ||
  213. (p2_x1 - 1 == p1_x2);
  214. }
  215.  
  216. void snap_helper::calc_dest( const RECT rc_wnd, const RECT rc_snap, int& dx1, int& dx2, int& dy1, int& dy2 )
  217. {
  218. // этот код допускает что правый край окна может притянуться
  219. // к левому краю десктопа, верхний -- к нижнему и т.д.
  220. // Т.о. окно становиться невидимым
  221. /*
  222. dx1 = nearest_dest( rc_snap.left, rc_snap.right, rc_wnd.left );
  223. dx2 = nearest_dest( rc_snap.left, rc_snap.right, rc_wnd.right );
  224. dy1 = nearest_dest( rc_snap.top, rc_snap.bottom, rc_wnd.top );
  225. dy2 = nearest_dest( rc_snap.top, rc_snap.bottom, rc_wnd.bottom );
  226. */
  227.  
  228. dx1 = rc_snap.left - rc_wnd.left;
  229. dx2 = rc_snap.right - rc_wnd.right;
  230. dy1 = rc_snap.top - rc_wnd.top;
  231. dy2 = rc_snap.bottom - rc_wnd.bottom;
  232. }
  233.  
  234. void snap_helper::calc_min_dest( const RECT rc_wnd, const RECT rc_snap, int& dx1, int& dx2, int& dy1, int& dy2 )
  235. {
  236. const int& e = s_thresold;
  237.  
  238. if( is_intersect( rc_wnd.left-e, rc_wnd.right+e, rc_snap.left-e, rc_snap.right+e ) )
  239. {
  240. int _dy1 = nearest_dest( rc_snap.top, rc_snap.bottom, rc_wnd.top );
  241. int _dy2 = nearest_dest( rc_snap.top, rc_snap.bottom, rc_wnd.bottom );
  242. if( abs(_dy1) < abs(dy1) ) dy1 = _dy1;
  243. if( abs(_dy2) < abs(dy2) ) dy2 = _dy2;
  244. }
  245.  
  246. if( is_intersect( rc_wnd.top-e, rc_wnd.bottom+e, rc_snap.top-e, rc_snap.bottom+e ) )
  247. {
  248. int _dx1 = nearest_dest( rc_snap.left, rc_snap.right, rc_wnd.left );
  249. int _dx2 = nearest_dest( rc_snap.left, rc_snap.right, rc_wnd.right );
  250. if( abs(_dx1) < abs(dx1) ) dx1 = _dx1;
  251. if( abs(_dx2) < abs(dx2) ) dx2 = _dx2;
  252. }
  253. }
  254.  
  255. void snap_helper::find_nearest_dst( HWND wnd, const RECT& rc_base, const RECT& rc_start
  256. , int& dx1, int& dx2, int& dy1, int& dy2 )
  257. {
  258. RECT rc_curr;
  259.  
  260. calc_dest( rc_base, rc_start, dx1, dx2, dy1, dy2 );
  261.  
  262. container_wnd::iterator i = s_wnd_list.begin();
  263.  
  264. for( ; i != s_wnd_list.end(); i++ )
  265. {
  266. if( i->m_wnd == wnd ) continue;
  267. //if( is_master && i->is_neighbour ) continue;
  268.  
  269. ::GetWindowRect( i->m_wnd, &rc_curr );
  270. calc_min_dest( rc_base, rc_curr, dx1, dx2, dy1, dy2 );
  271. }
  272. }
  273.  
  274. void snap_helper::find_nearest_dst_master( HWND wnd, const RECT& rc_base, const RECT& rc_start
  275. , int& dx1, int& dx2, int& dy1, int& dy2
  276. , int nb_off_x, int nb_off_y )
  277. {
  278. RECT rc_curr;
  279. calc_dest( rc_base, rc_start, dx1, dx2, dy1, dy2 );
  280.  
  281. container_wnd::iterator i = s_wnd_list.begin();
  282.  
  283. for( ; i != s_wnd_list.end(); i++ )
  284. {
  285. if( i->m_wnd == wnd ) continue;
  286. if( i->m_is_neighbour )
  287. {
  288. container_wnd::iterator j = s_wnd_list.begin();
  289.  
  290. RECT rc_neighbour;
  291. ::GetWindowRect( i->m_wnd, &rc_neighbour );
  292. ::OffsetRect( &rc_neighbour, nb_off_x, nb_off_y );
  293.  
  294. calc_min_dest( rc_neighbour, rc_start, dx1, dx2, dy1, dy2 );
  295.  
  296. for( ; j != s_wnd_list.end(); j++ )
  297. {
  298. if( j->m_wnd == wnd ) continue;
  299. if( j->m_wnd == i->m_wnd ) continue;
  300. if( j->m_is_neighbour ) continue;
  301.  
  302. ::GetWindowRect( j->m_wnd, &rc_curr );
  303. calc_min_dest( rc_neighbour, rc_curr, dx1, dx2, dy1, dy2 );
  304. }
  305. }
  306. else
  307. {
  308. ::GetWindowRect( i->m_wnd, &rc_curr );
  309. calc_min_dest( rc_base, rc_curr, dx1, dx2, dy1, dy2 );
  310. }
  311. }
  312. }
  313.  
  314. void snap_helper::find_nearest_destop_rect( const RECT& rc_wnd, RECT& rc_out )
  315. {
  316. HMONITOR hMonitor = MonitorFromRect( &rc_wnd, MONITOR_DEFAULTTONEAREST );
  317.  
  318. MONITORINFO mi; mi.cbSize = sizeof( MONITORINFO );
  319. GetMonitorInfo( hMonitor, &mi );
  320.  
  321. rc_out = mi.rcWork;
  322.  
  323. //find_nearest_rect( rc_wnd, mi.rcWork, mi.rcMonitor, rc_out );
  324. }
  325.  
  326. void snap_helper::edge_to_anchor( int edge, DRAG_ANCHOR_X& anc_x, DRAG_ANCHOR_Y& anc_y )
  327. {
  328. anc_x = DAX_UNKNOWN;
  329. anc_y = DAY_UNKNOWN;
  330.  
  331. if( edge == WMSZ_LEFT || edge == WMSZ_TOPLEFT || edge == WMSZ_BOTTOMLEFT ) anc_x = DAX_LEFT;
  332. else if( edge == WMSZ_RIGHT || edge == WMSZ_TOPRIGHT || edge == WMSZ_BOTTOMRIGHT ) anc_x = DAX_RIGHT;
  333.  
  334. if( edge == WMSZ_TOP || edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT ) anc_y = DAY_TOP;
  335. else if( edge == WMSZ_BOTTOM || edge == WMSZ_BOTTOMLEFT || edge == WMSZ_BOTTOMRIGHT ) anc_y = DAY_BOTTOM;
  336. }
  337.  
  338. LONG& snap_helper::rect_side_x( RECT& rc, DRAG_ANCHOR_X anc_x )
  339. {
  340. if( anc_x == DAX_RIGHT ) return rc.right;
  341. return rc.left;
  342. }
  343.  
  344. LONG& snap_helper::rect_side_y( RECT& rc, DRAG_ANCHOR_Y anc_y )
  345. {
  346. if( anc_y == DAY_BOTTOM ) return rc.bottom;
  347. return rc.top;
  348. }
  349.  
  350. bool snap_helper::is_neighbours( const RECT& rc1, const RECT& rc2 )
  351. {
  352. if( is_intersect( rc1.left, rc1.right, rc2.left, rc2.right ) )
  353. {
  354. if( rc1.top == rc2.top || rc1.top == rc2.bottom+0 ||
  355. rc1.bottom == rc2.bottom || rc1.bottom == rc2.top-0 ) return true;
  356. }
  357.  
  358. if( is_intersect( rc1.top, rc1.bottom, rc2.top, rc2.bottom ) )
  359. {
  360.  
  361. if( rc1.left == rc2.left || rc1.left == rc2.right+0 ||
  362. rc1.right == rc2.right || rc1.right == rc2.left-0 ) return true;
  363. }
  364.  
  365. return false;
  366. }
  367.  
  368. bool snap_helper::is_neighbours( HWND wnd1, HWND wnd2 )
  369. {
  370. RECT rc1, rc2;
  371. ::GetWindowRect( wnd1, &rc1 );
  372. ::GetWindowRect( wnd2, &rc2 );
  373.  
  374. return is_neighbours( rc1, rc2 );
  375. }
  376.  
  377. void snap_helper::mark_neighbours( HWND master, container_wnd& wnd_list )
  378. {
  379. container_wnd::iterator i = wnd_list.begin();
  380.  
  381. for( ; i != wnd_list.end(); i++ )
  382. {
  383. if( i->m_wnd == master ) continue;
  384. if( !i->m_is_neighbour && is_neighbours( i->m_wnd, master ) )
  385. {
  386. i->m_is_neighbour = true;
  387. mark_neighbours( i->m_wnd, wnd_list );
  388. }
  389. }
  390. }
  391.  
  392. void snap_helper::recalc_neighbours( HWND master, container_wnd& wnd_list )
  393. {
  394. container_wnd::iterator i = wnd_list.begin();
  395.  
  396. for( ; i != wnd_list.end(); i++ )
  397. {
  398. i->m_is_neighbour = false;
  399. }
  400.  
  401. mark_neighbours( master, wnd_list );
  402. }
  403.  
  404. /*
  405. void snap_helper::shift_window( HWND wnd, int dx, int dy )
  406. {
  407. RECT rc;
  408. ::GetWindowRect( wnd, &rc );
  409. ::OffsetRect( &rc, dx, dy );
  410.  
  411. ::MoveWindow( wnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, TRUE );
  412. }
  413. */
  414.  
  415. void snap_helper::shift_neighbour_windows( HWND wnd, container_wnd& wnd_list, int dx, int dy )
  416. {
  417. container_wnd::iterator i = wnd_list.begin();
  418.  
  419. int n_wnd = 0;
  420.  
  421. /*
  422. for( ; i != wnd_list.end(); i++ )
  423. {
  424. if( i->m_wnd == wnd ) continue;
  425. if( i->m_is_neighbour )
  426. shift_window( i->m_wnd, dx, dy );
  427. }
  428.  
  429. return;
  430. */
  431.  
  432. for( ; i != wnd_list.end(); i++ )
  433. {
  434. if( i->m_is_neighbour || i->m_wnd == wnd ) n_wnd++;
  435. }
  436.  
  437. HDWP h_dwp = ::BeginDeferWindowPos( n_wnd );
  438.  
  439. i = wnd_list.begin();
  440. for( ; i != wnd_list.end(); i++ )
  441. {
  442. if( i->m_is_neighbour || i->m_wnd == wnd )
  443. {
  444. RECT rc;
  445. ::GetWindowRect( i->m_wnd, &rc );
  446.  
  447. ::DeferWindowPos( h_dwp, i->m_wnd, 0, rc.left + dx, rc.top +dy, 0, 0, SWP_NOSIZE );
  448. }
  449. }
  450.  
  451. ::EndDeferWindowPos( h_dwp );
  452. }
  453.  
  454. void snap_helper::show_neighbour_windows( HWND wnd, container_wnd& wnd_list )
  455. {
  456. container_wnd::iterator i = wnd_list.begin();
  457. for( ; i != wnd_list.end(); i++ )
  458. {
  459. if( i->m_wnd == wnd ) continue;
  460. if( i->m_is_neighbour )
  461. {
  462. ::SetWindowPos( i->m_wnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
  463. }
  464. }
  465. }
  466.  
  467. void snap_helper::show_all_windows( HWND wnd, container_wnd& wnd_list )
  468. {
  469. container_wnd::iterator i = wnd_list.begin();
  470. for( ; i != wnd_list.end(); i++ )
  471. {
  472. if( i->m_wnd == wnd ) continue;
  473. ::SetWindowPos( i->m_wnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
  474. }
  475. }
  476.  
  477. //////////////////////////////////////////////////////////////////////////
  478.  
  479. void snap_helper::on_sizing( RECT* p_rc_new, int edge )
  480. {
  481. edge_to_anchor( edge, m_drag_anchor_x, m_drag_anchor_y );
  482.  
  483. LONG& side_x = rect_side_x( *p_rc_new, m_drag_anchor_x );
  484. LONG& side_y = rect_side_y( *p_rc_new, m_drag_anchor_y );
  485.  
  486. side_x += m_drag_err_x;
  487. side_y += m_drag_err_y;
  488.  
  489. RECT rc_nearest;
  490. find_nearest_destop_rect( *p_rc_new, rc_nearest );
  491.  
  492. int dx1, dx2, dy1, dy2;
  493. find_nearest_dst( m_wnd, *p_rc_new, rc_nearest, dx1, dx2, dy1, dy2 );
  494.  
  495. int dx = 0;
  496. int dy = 0;
  497.  
  498. switch( m_drag_anchor_x )
  499. {
  500. default: break;
  501. case DAX_LEFT:
  502. dx = is_close(dx1) ? dx1 : 0; break;
  503. case DAX_RIGHT:
  504. dx = is_close(dx2) ? dx2 : 0; break;
  505. }
  506.  
  507. switch( m_drag_anchor_y )
  508. {
  509. default: break;
  510. case DAY_TOP:
  511. dy = is_close(dy1) ? dy1 : 0; break;
  512. case DAY_BOTTOM:
  513. dy = is_close(dy2) ? dy2 : 0; break;
  514. }
  515.  
  516. side_x += dx;
  517. side_y += dy;
  518.  
  519. m_drag_err_x = dx;
  520. m_drag_err_y = dy;
  521. }
  522.  
  523. void snap_helper::on_moving( RECT* p_rc_new )
  524. {
  525. ::OffsetRect( p_rc_new, m_drag_err_x, m_drag_err_y );
  526.  
  527. RECT rc_curr;
  528. ::GetWindowRect( m_wnd, &rc_curr );
  529.  
  530. int dx = p_rc_new->left - rc_curr.left;
  531. int dy = p_rc_new->top - rc_curr.top;
  532.  
  533. int dx1, dx2, dy1, dy2;
  534. RECT rc_nearest;
  535. find_nearest_destop_rect( *p_rc_new, rc_nearest );
  536.  
  537.  
  538. if( is_master() )
  539. find_nearest_dst_master( m_wnd, *p_rc_new, rc_nearest, dx1, dx2, dy1, dy2, dx, dy );
  540. else
  541. find_nearest_dst( m_wnd, *p_rc_new, rc_nearest, dx1, dx2, dy1, dy2 );
  542.  
  543. dx = abs(dx1) < abs(dx2) ? dx1 : dx2;
  544. dy = abs(dy1) < abs(dy2) ? dy1 : dy2;
  545.  
  546. dx = is_close(dx) ? dx : 0;
  547. dy = is_close(dy) ? dy : 0;
  548.  
  549. ::OffsetRect( p_rc_new, dx, dy );
  550.  
  551. if( is_master() )
  552. {
  553. shift_neighbour_windows( m_wnd, s_wnd_list, p_rc_new->left-rc_curr.left, p_rc_new->top-rc_curr.top );
  554. }
  555.  
  556. m_drag_err_x = -dx;
  557. m_drag_err_y = -dy;
  558. }
  559.  
  560. void snap_helper::on_enter_sizemove()
  561. {
  562. recalc_neighbours( m_wnd, s_wnd_list );
  563. show_neighbour_windows( m_wnd, s_wnd_list );
  564.  
  565. m_drag_anchor_x = DAX_UNKNOWN;
  566. m_drag_anchor_y = DAY_UNKNOWN;
  567.  
  568. m_drag_err_x = 0;
  569. m_drag_err_y = 0;
  570. }
  571.  
  572. void snap_helper::on_activate()
  573. {
  574. recalc_neighbours( m_wnd, s_wnd_list );
  575. //show_neighbour_windows( m_wnd, s_wnd_list );
  576. show_all_windows( m_wnd, s_wnd_list );
  577. }
Класс для организации прилипающих окон, наподобии тех, что в Винампе.
Собственно, функционально он повторяет поведение Винампа - есть мастер-окно, которое при перетаскивании будет тянуть соседние окна, остальные окошки прилипают друг к другу.
Использоваться может в любом WinApi проекте.

Найденно на http://rsdn.ru/Forum/message/2723661.flat.aspx.

+добавить реализацию