KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Роджер Джек - Исчерпывающее руководство по написанию всплывающих подсказок

Роджер Джек - Исчерпывающее руководство по написанию всплывающих подсказок

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн "Роджер Джек - Исчерпывающее руководство по написанию всплывающих подсказок". Жанр: Программирование издательство неизвестно, год неизвестен.
Перейти на страницу:

Класс CTitleTipListBox отвечает за управление элементом TitleTip (см. рис.12). В переменной CTitleTipListBox::m_LastMouseMovePoint хранится последняя позиция курсора мыши. CTitleTipListBox::m_bMouseCaptured показывает, производится ли в данный момент захват мыши (mouse capture). CTitleTipListBox::m_TitleTip – это экземпляр класса CTitleTip, указывающий на показываемую подсказку. CTitleTipListBox::m_nNoIndex – это константа, означающая, что в элементе "список" не отображается подсказка ни для одной строки.

Рис.12. CTitleTipListBox

// TitleTipListBox.h : header file

//

/////////////////////////////////////////////////////////////////////////////

// CTitleTipListBox window

#ifndef __TITLETIPLISTBOX_H__

#define __TITLETIPLISTBOX_H__

#include "TitleTip.h"


class CTitleTipListBox : public CListBox { // Construction public:

 CTitleTipListBox();

// Overrides

 // ClassWizard generated virtual function overrides

 //{{AFX_VIRTUAL(CTitleTipListBox)

public:

 virtual BOOL PreTranslateMessage(MSG* pMsg);

 //}}AFX_VIRTUAL

 // Implementation

public:

 virtual ~CTitleTipListBox();

protected:

 const int m_nNoIndex; // Пустой индекс

 CPoint m_LastMouseMovePoint; // Последние координаты курсора мыши

 BOOL m_bMouseCaptured; // Захвачена ли мышь?

 CTitleTip m_TitleTip; // Показываемый элемент TitleTip

 // Этот метод должен быть переопределен элементом "список" с пользовательской отрисовкой.

 virtual int GetIdealItemRect(int nIndex, LPRECT lpRect);

 void AdjustTitleTip(int nNewIndex);

 void CaptureMouse();

 BOOL IsAppActive();

 // Generated message map functions

protected:

 //{{AFX_MSG(CTitleTipListBox)

 afx_msg void OnMouseMove(UINT nFlags, CPoint point);

 afx_msg void OnSelchange();

 afx_msg void OnKillFocus(CWnd* pNewWnd);

 afx_msg void OnDestroy();

 afx_msg void OnLButtonDown(UINT nFlags, CPoint point);

 afx_msg void OnLButtonUp(UINT nFlags, CPoint point);

 //}}AFX_MSG

 afx_msg LONG OnContentChanged(UINT, LONG);

 DECLARE_MESSAGE_MAP()

};

#endif // __TITLETIPLISTBOX_H__


/////////////////////////////////////////////////////////////////////////////

// TitleTipListBox.cpp : implementation file

//

#include "stdafx.h"

#include "TitleTipListBox.h"


#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE static char THIS_FILE[] = __FILE__;

#endif


/////////////////////////////////////////////////////////////////////////////

// CTitleTipListBox

CTitleTipListBox::CTitleTipListBox() : m_LastMouseMovePoint(0, 0) , m_nNoIndex(-1) {

 m_bMouseCaptured = FALSE;

}


CTitleTipListBox::~CTitleTipListBox() {

 ASSERT(!m_bMouseCaptured);

}


int CTitleTipListBox::GetIdealItemRect(int nIndex, LPRECT lpRect) {

 // Вычислить размеры идеальной строки. Размеры зависят

 // от длины строки. Это работает только для обычных элементов

 // "список"(без пользовательской отрисовки)

 ASSERT(lpRect);

 ASSERT(nIndex >= 0);

 DWORD dwStyle = GetStyle();

 int nStatus = GetItemRect(nIndex, lpRect);

 if (nStatus != LB_ERR && !(dwStyle & LBS_OWNERDRAWFIXED) && !(dwStyle & LBS_OWNERDRAWVARIABLE)) {

  CString strItem;

  GetText(nIndex, strItem);

  if (!strItem.IsEmpty()) {

   // Вычислить длину идеального текста.

   CClientDC DC(this);

   CFont* pOldFont = DC.SelectObject(GetFont());

   CSize ItemSize = DC.GetTextExtent(strItem);

   DC.SelectObject(pOldFont);

   // Взять максимум от обычной ширины и идеальной ширины.

   const int cxEdgeSpace = 2;

   lpRect->right = max(lpRect->right, lpRect->left + ItemSize.cx + (cxEdgeSpace * 2));

  }

 } else {

  TRACE("Owner-draw listbox detected – override CTitleTipListBox::GetIdeaItemRect()n");

 }

 return nStatus;

}


void CTitleTipListBox::AdjustTitleTip(int nNewIndex) {

 if (!::IsWindow(m_TitleTip.m_hWnd)) {

  VERIFY(m_TitleTip.Create(this));

 }

 if (nNewIndex == m_nNoIndex) {

  m_TitleTip.Hide();

 } else {

  CRect IdealItemRect;

  GetIdealItemRect(nNewIndex, IdealItemRect);

  CRect ItemRect;

  GetItemRect(nNewIndex, ItemRect);

  if (ItemRect == IdealItemRect) {

   m_TitleTip.Hide();

  } else {

   // Поправить координаты рядом с краем экрана.

   ClientToScreen(IdealItemRect);

   int nScreenWidth = ::GetSystemMetrics(SM_CXFULLSCREEN);

   if (IdealItemRect.right > nScreenWidth) {

    IdealItemRect.OffsetRect(nScreenWidth – IdealItemRect.right, 0);

   }

   if (IdealItemRect.left < 0) {

    IdealItemRect.OffsetRect(-IdealItemRect.left, 0);

   }

   m_TitleTip.Show(IdealItemRect, nNewIndex);

  }

 }

 if (m_TitleTip.IsWindowVisible()) {

  // Удостовериться, что мышь захвачена, чтобы отследить

  // момент отключения подсказки.

  if (!m_bMouseCaptured && GetCapture() != this) {

   CaptureMouse();

  }

 } else {

  // Подсказка невидима, поэтому освободить мышь.

  if (m_bMouseCaptured) {

   VERIFY(ReleaseCapture());

   m_bMouseCaptured = FALSE;

  }

 }

}


void CTitleTipListBox::CaptureMouse() {

 ASSERT(!m_bMouseCaptured);

 CPoint Point;

 VERIFY(GetCursorPos(&Point));

 ScreenToClient(&Point);

 m_LastMouseMovePoint = Point;

 SetCapture();

 m_bMouseCaptured = TRUE;

}


/////////////////////////////////////////////////////////////////////////////

// CTitleTipListBox message handlers

LONG CTitleTipListBox::OnContentChanged(UINT, LONG) {

 // Turn off title tip.

 AdjustTitleTip(m_nNoIndex);

 return Default();

}


void CTitleTipListBox::OnMouseMove(UINT nFlags, CPoint point) {

 if (point != m_LastMouseMovePoint && IsAppActive()) {

  m_LastMouseMovePoint = point;

  int nIndexHit = m_nNoIndex;

  CRect ClientRect;

  GetClientRect(ClientRect);

  if (ClientRect.PtInRect(point)) {

   // Hit test.

   for (int n = 0; nIndexHit == m_nNoIndex && n < GetCount(); n++) {

    CRect ItemRect;

    GetItemRect(n, ItemRect);

    if (ItemRect.PtInRect(point)) {

     nIndexHit = n;

    }

   }

  }

  AdjustTitleTip(nIndexHit);

 }

 CListBox::OnMouseMove(nFlags, point);

}


void CTitleTipListBox::OnSelchange() {

 int nSelIndex;

 if (GetStyle() & LBS_MULTIPLESEL) {

  nSelIndex = GetCaretIndex();

 } else {

  nSelIndex = GetCurSel();

 }

 AdjustTitleTip(nSelIndex);

 m_TitleTip.InvalidateRect(NULL);

 m_TitleTip.UpdateWindow();

}


void CTitleTipListBox::OnKillFocus(CWnd* pNewWnd) {

 CListBox::OnKillFocus(pNewWnd);

 if (pNewWnd != &m_TitleTip) {

  AdjustTitleTip(m_nNoIndex);

 }

}


void CTitleTipListBox::OnDestroy() {

 AdjustTitleTip(m_nNoIndex);

 m_TitleTip.DestroyWindow();

 CListBox::OnDestroy();

}


void CTitleTipListBox::OnLButtonDown(UINT nFlags, CPoint point) {

 // Временно отключить захват мыши, так как базовый класс может

 // захватить мышь.

 if (m_bMouseCaptured) {

  ReleaseCapture();

  m_bMouseCaptured = FALSE;

 }

 CListBox::OnLButtonDown(nFlags, point);

 if (m_TitleTip.IsWindowVisible()) {

  m_TitleTip.InvalidateRect(NULL);

  if (this != GetCapture()) {

   CaptureMouse();

  }

 }

}


void CTitleTipListBox::OnLButtonUp(UINT nFlags, CPoint point) {

 CListBox::OnLButtonUp(nFlags, point);

 if (this != GetCapture() && m_TitleTip.IsWindowVisible()) {

  CaptureMouse();

 }

}


BOOL CTitleTipListBox::PreTranslateMessage(MSG* pMsg) {

 switch (pMsg->message) {

 case WM_RBUTTONDOWN:

 case WM_RBUTTONUP:

 case WM_LBUTTONDBLCLK:

 case WM_RBUTTONDBLCLK:

 // Активизировать окно представления, потому что такое

 // поведение подразумевается по сообщению WM_MOUSEACTIVATE,

 // когда над окном нет никаких подсказок.

  AdjustTitleTip(m_nNoIndex);

  CFrameWnd* pFrameWnd = GetParentFrame();

  if (pFrameWnd) {

   BOOL bDone = FALSE;

   CWnd* pWnd = this;

   while (!bDone) {

    pWnd = pWnd->GetParent();

    if (!pWnd || pWnd == pFrameWnd) {

     bDone = TRUE;

    }

    else if (pWnd->IsKindOf(RUNTIME_CLASS(CView))) {

     pFrameWnd->SetActiveView((CView*)pWnd);

     bDone = TRUE;

    }

   }

  }

  break;

 }

 return CListBox::PreTranslateMessage(pMsg);

}

Функция CTitleTipListBox::GetIdealItemRect вычисляет размер и координаты идеальной строки списка. Параметр nIndex – это индекс нужной строки. Параметр lpRect используется для того, чтобы вернуть идеальный размер и координаты в клиентской системе координат. Вы должны переопределить этот метод для элемента "список" с пользовательской отрисовкой, и далее я покажу, как с этим справляется CODListBox. Если не переопределить этот метод для элемента "список" с пользовательской отрисовкой, то метод CTitleTipListBox::GetIdealItemRect выдаст TRACE-сообщение об ошибке. Однако для обычных элементов "список" этот метод автоматически вычисляет размер и координаты идеальной строки списка. Сначала он вызывает функцию CListBox::GetItemRect для вычисления высоты и ширины строки. Ширина строки, возвращенная CListBox::GetItemRect является шириной самого элемента "список", а не шириной текста. Чтобы вычислить настоящую ширину текста подсказки, я получаю текст и шрифт для строки и вызываю CDC::GetTextExtent. Затем в lpRect подставляется максимум от ширины строки и вычисленной ширины строки (плюс немного места по краям из эстетических соображений).

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*