Creating Sticky Windows | CodeGuru

Creating Sticky Windows

Click here for a larger image. Environment: VC6 I’ve wanted to implement a variation on the constrained window alignment available in popular software titles like Winamp and CorelDRAW for some time and finally got the opportunity. Although the test application is MFC, the core functionality is contained in the non-MFC class CStickyDragManager. CStickyDragManager handles the […]

Written By
CodeGuru Staff
CodeGuru Staff
Feb 7, 2003
1 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More



Click here for a larger image.

Environment: VC6

I’ve wanted to implement a variation on the constrained window alignment available in popular software titles like Winamp and CorelDRAW for some time and finally got the opportunity. Although the test application is MFC, the core functionality is contained in the non-MFC class CStickyDragManager.

CStickyDragManager handles the calculation of window bounding rectangles relative to the position of other registered windows. Typically, the calling application invokes the window rectangle calculation phase during a WM_SIZING, a WM_MOVING message, or in a synthetic window dragger (for example, a MFC controlbar dragging code). CStickyDragManager also handles drag grouping by nominating a window candidate as a “Docker.” Docker windows can drag all adjacent registered windows.

//StickyDragManager.h…
#include <windows.h>
    #include <vector>
    using namespace std;
    class CStickyDragManager
    {
    public:
      CStickyDragManager(bool isDocker = false);
      ~CStickyDragManager();
      void Init(HWND hWnd = 0);
                   vector<RECT>& dockedRects);
      void SnapMoveRect(LPRECT r);
      void SnapSize(LPRECT r);
      void GetDockedWindows(HWND hWnd, int index,
           vector<HWND> &windows);
      void StartTrack();
      inline void SetDockerState(bool bDockerState) {
             m_bIsDocker = bDockerState; }
      vector<HWND> *SnapMove(LPRECT activeRect,
      inline void AddWindow(HWND hWnd) {
             m_windows.push_back(hWnd); }
      inline HWND GetActiveWindow() { return m_activeWindow; }
      inline bool IsCloseTo(int ms, int ss) { return ((ms
             > (ssm_slack)) && (ms < (ss + m_slack)))
             ? true : false; }
      inline vector<HWND>* DockedWindows() { return
             &m_dockedWindows; }
    protected:
      RECT m_lastDragRect;
      bool m_bIsDocker;
      POINT m_dragPos;
      int m_slack;
      bool m_stickFlags[4];
      int m_stickPos[4];
      HWND m_activeWindow;
      HWND m_exclusionWindow;
      vector<HWND> m_windows;
      vector<HWND> m_dockedWindows;
      vector<bool> m_windowDock;
      vector<bool> m_windowDockProcess;
    };
//StickyDragManager.cpp…
#include “StickyDragManager.h”
    CStickyDragManager::CStickyDragManager(bool isDocker)
    {
      Init();
      m_bIsDocker = isDocker;
    }
    CStickyDragManager::~CStickyDragManager()
    {
    }
    void CStickyDragManager::Init(HWND hWnd)
    {
      m_exclusionWindow = 0;
      m_activeWindow = hWnd;
      m_dockedWindows.clear();
      m_windows.clear();
      m_slack = 5; // slack determines how strongly attracted
                   // registered windows are to each other.
      for (int i = 0; i < 4; i++)
      {
        m_stickFlags[i] = false;
        m_stickPos[i] = 0;
      }
    }
    vector<HWND> *CStickyDragManager::SnapMove(LPRECT
                                      activeRect,
    vector<RECT>& dockedRects)
    {
      int dx = 0, dy = 0;
      RECT ssar, sar, sr;
      dockedRects.clear();
      ssar = *activeRect;
      int dxy[2];
      dxy[0] = activeRect->left – m_lastDragRect.left;
      dxy[1] = activeRect->top – m_lastDragRect.top;
      if (!m_dockedWindows.size())
      {
        SnapMoveRect(activeRect);
        m_lastDragRect = *activeRect;
        return &m_dockedWindows;
      }
      for (unsigned int i = 0; i < m_dockedWindows.size(); i++)
      {
        RECT r;
        GetWindowRect(m_dockedWindows[i], &r);
        OffsetRect(&r, dxy[0], dxy[1]);
        dockedRects.push_back(r);
      }
      do
      {
        sar = *activeRect;
        SnapMoveRect(activeRect);
        dx = sar.left – activeRect->left;
        dy = sar.top – activeRect->top;
        for (unsigned i = 0; i < m_dockedWindows.size(); i++)
        {
          OffsetRect(&dockedRects[i], -dx, -dy);
          sr = dockedRects[i];
          SnapMoveRect(&dockedRects[i]);
          dx = sr.left – dockedRects[i].left;
          dy = sr.top – dockedRects[i].top;
          for (unsigned int j = 0; j < m_dockedWindows.size();
                                   j++)
            if (j != i)
              OffsetRect(&dockedRects[j], -dx, -dy);
              OffsetRect(activeRect, -dx, -dy);
        }
        dx = sar.left – activeRect->left;
        dy = sar.top – activeRect->top;
      }
      while (dx || dy);
      dx = ssar.left – activeRect->left;
      dy = ssar.top – activeRect->top;
      for (i = 0; i < m_dockedWindows.size(); i++)
      {
        GetWindowRect(m_dockedWindows[i], &dockedRects[i]);
        OffsetRect(&dockedRects[i], dxy[0]-dx, dxy[1]-dy);
      }
      m_lastDragRect = *activeRect;
      return &m_dockedWindows;
    }
    void CStickyDragManager::SnapMoveRect(LPRECT r)
    {
      POINT cPos;
      GetCursorPos(&cPos);
      int mPos[4] = {cPos.x, cPos.y, cPos.x, cPos.y};
      for (int j = 0; j < 4; j++)
      {
        if (m_stickFlags[j])
        {
          if (abs(m_stickPos[j] – mPos[j]) > 2 * m_slack)
          {
            m_stickFlags[j] = false;
            int d = mPos[j] – m_stickPos[j];
            switch(j)
              {
                case 0: OffsetRect(r, d, 0); break;
                case 1: OffsetRect(r, 0, d); break;
                case 2: OffsetRect(r, d, 0); break;
                case 3: OffsetRect(r, 0, d); break;
              }
          }
        }
      }
      int dx = 0;
      int dy = 0;
      int iPos = -1;
      int jPos = -1;
      RECT cr, dr, sr;
      for (unsigned int i = 0; i < m_windows.size(); i++)
      {
        if (m_windows[i] == m_exclusionWindow)
          continue;
        GetWindowRect(m_windows[i], &cr);
        sr = cr;
        InflateRect(&sr, m_slack, m_slack);
        if (!IntersectRect(&dr, &sr, r))
          continue;
        bool b1 = false;
        if ((b1 = IsCloseTo(r->right, cr.left)) ||
                 (IsCloseTo(r->right, cr.right)))
        {
          dx = r->right – (b1 ? cr.left : cr.right); iPos = 2;
        }
        else if ((b1 = IsCloseTo(r->left, cr.left)) ||
                      (IsCloseTo(r->left, cr.right)))
        {
          dx = r->left – (b1 ? cr.left : cr.right); iPos = 0;
        }
        if ((b1 = IsCloseTo(r->top, cr.top)) ||
                 (IsCloseTo(r->top, cr.bottom)))
        {
          dy = r->top – (b1 ? cr.top : cr.bottom); jPos = 1;
        }
        else if ((b1 = IsCloseTo(r->bottom, cr.top)) ||
                      (IsCloseTo(r->bottom, cr.bottom)))
        {
          dy = r->bottom – (b1 ? cr.top : cr.bottom); jPos = 3;
        }
        OffsetRect(r, -dx, -dy);
        if (iPos > -1 && !m_stickFlags[iPos])
        {
          m_stickFlags[iPos] = true;
          m_stickPos[iPos] = mPos[iPos];
        }
        if (jPos > -1 && !m_stickFlags[jPos])
        {
          m_stickFlags[jPos] = true;
          m_stickPos[jPos] = mPos[jPos];
        }
        if (dx || dy)
          break;
      }
    }
    void CStickyDragManager::SnapSize(LPRECT r)
    {
      RECT cr, dr, sr;
      for (unsigned int i = 0; i < m_windows.size(); i++)
      {
        GetWindowRect(m_windows[i], &cr);
        sr = cr;
        InflateRect(&sr, m_slack, m_slack);
        if (!IntersectRect(&dr, &sr, r))
          continue;
        bool b1 = false;
        if ((b1 = IsCloseTo(r->right, cr.left)) ||
                 (IsCloseTo(r->right, cr.right)))
          r->right -= r->right – (b1 ? cr.left : cr.right);
        if ((b1 = IsCloseTo(r->left, cr.left)) ||
                 (IsCloseTo(r->left, cr.right)))
          r->left -= r->left – (b1 ? cr.left : cr.right);
        if ((b1 = IsCloseTo(r->top, cr.top)) ||
                 (IsCloseTo(r->top, cr.bottom)))
          r->top -= r->top – (b1 ? cr.top : cr.bottom);
        if ((b1 = IsCloseTo(r->bottom, cr.top)) ||
                 (IsCloseTo(r->bottom, cr.bottom)))
          r->bottom -= r->bottom – (b1 ? cr.top : cr.bottom);
       }
    }
    void CStickyDragManager::GetDockedWindows(HWND hWnd,
                             int index, vector<HWND>
                             &windows)
    {
      RECT wr, twr, dr;
      if (index == -1)
      {
        m_windowDock.clear();
        m_windowDockProcess.clear();
        for (unsigned int i = 0; i < m_windows.size(); i++)
        {
          m_windowDock.push_back(false);
          m_windowDockProcess.push_back(false);
        }
      }
    else
        m_windowDockProcess[index] = true;
    GetWindowRect((index == -1) ? hWnd : m_windows[index],
                                         &wr);
        for (unsigned int i = 0; i < m_windows.size(); i++)
        {
        if (i != index && !m_windowDock[i])
        {
          GetWindowRect(m_windows[i], &twr);
          RECT cwr = wr;
          InflateRect(&cwr, 1, 1);
            if (!IntersectRect(&dr, &twr, &cwr))
              continue;
          if ((twr.left == wr.left) || (twr.left == wr.right))
                                        m_windowDock[i] = true;
          if ((twr.right == wr.left) || (twr.right == wr.right))
                                        m_windowDock[i] = true;
          if ((twr.top == wr.top) || (twr.top == wr.bottom))
                                        m_windowDock[i] = true;
          if ((twr.bottom == wr.top) ||
             (twr.bottom == wr.bottom)) m_windowDock[i] = true;
          if (!m_windowDockProcess[i] && m_windowDock[i])
            GetDockedWindows(m_windows[i], i, windows);
        }
      }
      if (index == -1)
      {
        windows.clear();
        for (unsigned int i = 0; i < m_windows.size(); i++)
        if (m_windowDock[i])
          windows.push_back(m_windows[i]);
      }
    }
    void CStickyDragManager::StartTrack()
    {
    if (m_bIsDocker)
      {
        if (m_activeWindow)
          GetDockedWindows(m_activeWindow, -1, m_dockedWindows);
    // Exclude docked windows from dock candidates.
        vector<HWND> windows;
        for (vector<HWND>::iterator i = m_windows.begin();
                                    i != m_windows.end(); i++)
        {
          bool docked = false;
          for (unsigned int j = 0; j < m_dockedWindows.size();
                                   j++)
          if (*i == m_dockedWindows[j]) docked = true;
          if (!docked)
          windows.push_back(*i);
        }
        m_windows.clear();
        m_windows = windows;
      }
      GetWindowRect(m_activeWindow, &m_lastDragRect);
    }

Downloads

Download source – 10Kb

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.