mirror of
https://github.com/azure-rtos/guix.git
synced 2025-02-04 07:13:17 +08:00
497 lines
13 KiB
C++
497 lines
13 KiB
C++
|
|
#include "stdafx.h"
|
|
#include "ScrollHelper.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
// CScrollHelper /////////////////////////////////////////////////////////////////////
|
|
|
|
CScrollHelper::CScrollHelper()
|
|
{
|
|
m_attachWnd = NULL;
|
|
m_pageSize = CSize(0,0);
|
|
m_displaySize = CSize(0,0);
|
|
m_scrollPos = CSize(0,0);
|
|
}
|
|
|
|
CScrollHelper::~CScrollHelper()
|
|
{
|
|
DetachWnd();
|
|
}
|
|
|
|
void CScrollHelper::AttachWnd(CWnd* pWnd)
|
|
{
|
|
m_attachWnd = pWnd;
|
|
}
|
|
|
|
void CScrollHelper::DetachWnd()
|
|
{
|
|
m_attachWnd = NULL;
|
|
}
|
|
|
|
void CScrollHelper::SetDisplaySize(int displayWidth, int displayHeight)
|
|
{
|
|
m_displaySize = CSize(displayWidth, displayHeight);
|
|
|
|
if ( m_attachWnd != NULL && ::IsWindow(m_attachWnd->m_hWnd) )
|
|
UpdateScrollInfo();
|
|
}
|
|
|
|
const CSize& CScrollHelper::GetDisplaySize() const
|
|
{
|
|
return m_displaySize;
|
|
}
|
|
|
|
const CSize& CScrollHelper::GetScrollPos() const
|
|
{
|
|
return m_scrollPos;
|
|
}
|
|
|
|
|
|
const CSize& CScrollHelper::GetPageSize() const
|
|
{
|
|
return m_pageSize;
|
|
}
|
|
|
|
void CScrollHelper::SetScrollPos(int bar, int newpos, BOOL redraw)
|
|
{
|
|
if (m_attachWnd == NULL)
|
|
return;
|
|
|
|
CRect rect;
|
|
GetClientRectSB(m_attachWnd, rect);
|
|
CSize windowSize(rect.Width(), rect.Height());
|
|
|
|
if ((bar == SB_HORZ) && (windowSize.cx < m_displaySize.cx))
|
|
{
|
|
int deltaPos = newpos - m_scrollPos.cx;
|
|
m_scrollPos.cx += deltaPos;
|
|
m_attachWnd->SetScrollPos(SB_HORZ, m_scrollPos.cx, redraw);
|
|
if (redraw)
|
|
{
|
|
m_attachWnd->ScrollWindow(0, -deltaPos);
|
|
}
|
|
}
|
|
|
|
if ((bar == SB_VERT) && (windowSize.cy < m_displaySize.cy))
|
|
{
|
|
int deltaPos = newpos - m_scrollPos.cy;
|
|
m_scrollPos.cy += deltaPos;
|
|
m_attachWnd->SetScrollPos(SB_VERT, m_scrollPos.cy, redraw);
|
|
|
|
if (redraw)
|
|
{
|
|
m_attachWnd->ScrollWindow(0, -deltaPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CScrollHelper::ScrollToOrigin(bool scrollLeft, bool scrollTop)
|
|
{
|
|
if ( m_attachWnd == NULL )
|
|
return;
|
|
|
|
if ( scrollLeft )
|
|
{
|
|
if ( m_displaySize.cx > 0 && m_pageSize.cx > 0 && m_scrollPos.cx > 0 )
|
|
{
|
|
int deltaPos = -m_scrollPos.cx;
|
|
m_scrollPos.cx += deltaPos;
|
|
m_attachWnd->SetScrollPos(SB_HORZ, m_scrollPos.cx, TRUE);
|
|
m_attachWnd->ScrollWindow(-deltaPos, 0);
|
|
}
|
|
}
|
|
|
|
if ( scrollTop )
|
|
{
|
|
if ( m_displaySize.cy > 0 && m_pageSize.cy > 0 && m_scrollPos.cy > 0 )
|
|
{
|
|
int deltaPos = -m_scrollPos.cy;
|
|
m_scrollPos.cy += deltaPos;
|
|
m_attachWnd->SetScrollPos(SB_VERT, m_scrollPos.cy, TRUE);
|
|
m_attachWnd->ScrollWindow(0, -deltaPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CScrollHelper::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
|
{
|
|
if ( m_attachWnd == NULL )
|
|
return;
|
|
|
|
const int lineOffset = 60;
|
|
|
|
// Compute the desired change or delta in scroll position.
|
|
int deltaPos = 0;
|
|
switch( nSBCode )
|
|
{
|
|
case SB_LINELEFT:
|
|
// Left scroll arrow was pressed.
|
|
deltaPos = -lineOffset;
|
|
break;
|
|
|
|
case SB_LINERIGHT:
|
|
// Right scroll arrow was pressed.
|
|
deltaPos = lineOffset;
|
|
break;
|
|
|
|
case SB_PAGELEFT:
|
|
// User clicked inbetween left arrow and thumb.
|
|
deltaPos = -m_pageSize.cx;
|
|
break;
|
|
|
|
case SB_PAGERIGHT:
|
|
// User clicked inbetween thumb and right arrow.
|
|
deltaPos = m_pageSize.cx;
|
|
break;
|
|
|
|
case SB_THUMBTRACK:
|
|
// Scrollbar thumb is being dragged.
|
|
deltaPos = Get32BitScrollPos(SB_HORZ, pScrollBar) - m_scrollPos.cx;
|
|
break;
|
|
|
|
case SB_THUMBPOSITION:
|
|
// Scrollbar thumb was released.
|
|
deltaPos = Get32BitScrollPos(SB_HORZ, pScrollBar) - m_scrollPos.cx;
|
|
break;
|
|
|
|
default:
|
|
// We don't process other scrollbar messages.
|
|
return;
|
|
}
|
|
|
|
// Compute the new scroll position.
|
|
int newScrollPos = m_scrollPos.cx + deltaPos;
|
|
|
|
// If the new scroll position is negative, we adjust
|
|
// deltaPos in order to scroll the window back to origin.
|
|
if ( newScrollPos < 0 )
|
|
deltaPos = -m_scrollPos.cx;
|
|
|
|
// If the new scroll position is greater than the max scroll position,
|
|
// we adjust deltaPos in order to scroll the window precisely to the
|
|
// maximum position.
|
|
int maxScrollPos = m_displaySize.cx - m_pageSize.cx;
|
|
if ( newScrollPos > maxScrollPos )
|
|
deltaPos = maxScrollPos - m_scrollPos.cx;
|
|
|
|
// Scroll the window if needed.
|
|
if ( deltaPos != 0 )
|
|
{
|
|
m_scrollPos.cx += deltaPos;
|
|
m_attachWnd->SetScrollPos(SB_HORZ, m_scrollPos.cx, TRUE);
|
|
m_attachWnd->ScrollWindow(-deltaPos, 0);
|
|
}
|
|
}
|
|
|
|
void CScrollHelper::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
|
|
{
|
|
if ( m_attachWnd == NULL )
|
|
return;
|
|
|
|
const int lineOffset = 60;
|
|
|
|
// Compute the desired change or delta in scroll position.
|
|
int deltaPos = 0;
|
|
switch( nSBCode )
|
|
{
|
|
case SB_LINEUP:
|
|
// Up arrow button on scrollbar was pressed.
|
|
deltaPos = -lineOffset;
|
|
break;
|
|
|
|
case SB_LINEDOWN:
|
|
// Down arrow button on scrollbar was pressed.
|
|
deltaPos = lineOffset;
|
|
break;
|
|
|
|
case SB_PAGEUP:
|
|
// User clicked inbetween up arrow and thumb.
|
|
deltaPos = -m_pageSize.cy;
|
|
break;
|
|
|
|
case SB_PAGEDOWN:
|
|
// User clicked inbetween thumb and down arrow.
|
|
deltaPos = m_pageSize.cy;
|
|
break;
|
|
|
|
case SB_THUMBTRACK:
|
|
// Scrollbar thumb is being dragged.
|
|
deltaPos = Get32BitScrollPos(SB_VERT, pScrollBar) - m_scrollPos.cy;
|
|
break;
|
|
|
|
case SB_THUMBPOSITION:
|
|
// Scrollbar thumb was released.
|
|
deltaPos = Get32BitScrollPos(SB_VERT, pScrollBar) - m_scrollPos.cy;
|
|
break;
|
|
|
|
default:
|
|
// We don't process other scrollbar messages.
|
|
return;
|
|
}
|
|
|
|
// Compute the new scroll position.
|
|
int newScrollPos = m_scrollPos.cy + deltaPos;
|
|
|
|
// If the new scroll position is negative, we adjust
|
|
// deltaPos in order to scroll the window back to origin.
|
|
if ( newScrollPos < 0 )
|
|
deltaPos = -m_scrollPos.cy;
|
|
|
|
// If the new scroll position is greater than the max scroll position,
|
|
// we adjust deltaPos in order to scroll the window precisely to the
|
|
// maximum position.
|
|
int maxScrollPos = m_displaySize.cy - m_pageSize.cy;
|
|
if ( newScrollPos > maxScrollPos )
|
|
deltaPos = maxScrollPos - m_scrollPos.cy;
|
|
|
|
// Scroll the window if needed.
|
|
if ( deltaPos != 0 )
|
|
{
|
|
m_scrollPos.cy += deltaPos;
|
|
m_attachWnd->SetScrollPos(SB_VERT, m_scrollPos.cy, TRUE);
|
|
m_attachWnd->ScrollWindow(0, -deltaPos);
|
|
}
|
|
}
|
|
|
|
BOOL CScrollHelper::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
|
|
{
|
|
if ( m_attachWnd == NULL )
|
|
return FALSE;
|
|
|
|
// Don't do anything if the vertical scrollbar is not enabled.
|
|
int scrollMin = 0, scrollMax = 0;
|
|
m_attachWnd->GetScrollRange(SB_VERT, &scrollMin, &scrollMax);
|
|
if ( scrollMin == scrollMax )
|
|
return FALSE;
|
|
|
|
// Compute the number of scrolling increments requested.
|
|
int numScrollIncrements = abs(zDelta) / WHEEL_DELTA;
|
|
|
|
// Each scrolling increment corresponds to a certain number of
|
|
// scroll lines (one scroll line is like a SB_LINEUP or SB_LINEDOWN).
|
|
// We need to query the system parameters for this value.
|
|
int numScrollLinesPerIncrement = 0;
|
|
::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &numScrollLinesPerIncrement, 0);
|
|
|
|
// Check if a page scroll was requested.
|
|
if ( numScrollLinesPerIncrement == WHEEL_PAGESCROLL )
|
|
{
|
|
// Call the vscroll message handler to do the work.
|
|
OnVScroll(zDelta > 0 ? SB_PAGEUP : SB_PAGEDOWN, 0, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
// Compute total number of lines to scroll.
|
|
int numScrollLines = numScrollIncrements * numScrollLinesPerIncrement;
|
|
|
|
// Adjust numScrollLines to slow down the scrolling a bit more.
|
|
numScrollLines = max(numScrollLines/3, 1);
|
|
|
|
// Do the scrolling.
|
|
for(int i = 0; i < numScrollLines; ++i)
|
|
{
|
|
// Call the vscroll message handler to do the work.
|
|
OnVScroll(zDelta > 0 ? SB_LINEUP : SB_LINEDOWN, 0, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void CScrollHelper::OnSize(UINT nType, int cx, int cy)
|
|
{
|
|
UpdateScrollInfo();
|
|
}
|
|
|
|
BOOL CScrollHelper::Scroll(int bar, int delta)
|
|
{
|
|
int pos;
|
|
int new_pos;
|
|
int max_pos;
|
|
|
|
if (bar == SB_VERT)
|
|
{
|
|
pos = m_scrollPos.cy;
|
|
max_pos = m_displaySize.cy - m_pageSize.cy;
|
|
|
|
}
|
|
else if (bar == SB_HORZ)
|
|
{
|
|
pos = m_scrollPos.cx;
|
|
max_pos = m_displaySize.cx - m_pageSize.cx;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
new_pos = pos + delta;
|
|
|
|
if (delta > 0)
|
|
{
|
|
if (new_pos > max_pos)
|
|
{
|
|
new_pos = max_pos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (new_pos < 0)
|
|
{
|
|
new_pos = 0;
|
|
}
|
|
}
|
|
|
|
if (new_pos != pos)
|
|
{
|
|
SetScrollPos(bar, new_pos, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int CScrollHelper::Get32BitScrollPos(int bar, CScrollBar* pScrollBar)
|
|
{
|
|
// Code below is from MSDN Article ID 152252, "How To Get
|
|
// 32-bit Scroll Position During Scroll Messages".
|
|
|
|
// First determine if the user scrolled a scroll bar control
|
|
// on the window or scrolled the window itself.
|
|
ASSERT( m_attachWnd != NULL );
|
|
HWND hWndScroll;
|
|
if ( pScrollBar == NULL )
|
|
hWndScroll = m_attachWnd->m_hWnd;
|
|
else
|
|
hWndScroll = pScrollBar->m_hWnd;
|
|
|
|
SCROLLINFO si;
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.fMask = SIF_TRACKPOS;
|
|
::GetScrollInfo(hWndScroll, bar, &si);
|
|
|
|
int scrollPos = si.nTrackPos;
|
|
|
|
return scrollPos;
|
|
}
|
|
|
|
void CScrollHelper::UpdateScrollInfo()
|
|
{
|
|
if ( m_attachWnd == NULL )
|
|
return;
|
|
|
|
// Get the width/height of the attached wnd that includes the area
|
|
// covered by the scrollbars (if any). The reason we need this is
|
|
// because when scrollbars are present, both cx/cy and GetClientRect()
|
|
// when accessed from OnSize() do not include the scrollbar covered
|
|
// areas. In other words, their values are smaller than what you would
|
|
// expect.
|
|
CRect rect;
|
|
GetClientRectSB(m_attachWnd, rect);
|
|
CSize windowSize(rect.Width(), rect.Height());
|
|
|
|
// Update horizontal scrollbar.
|
|
CSize deltaPos(0,0);
|
|
UpdateScrollBar(SB_HORZ, windowSize.cx, m_displaySize.cx,
|
|
m_pageSize.cx, m_scrollPos.cx, deltaPos.cx);
|
|
|
|
// Update vertical scrollbar.
|
|
UpdateScrollBar(SB_VERT, windowSize.cy, m_displaySize.cy,
|
|
m_pageSize.cy, m_scrollPos.cy, deltaPos.cy);
|
|
|
|
// See if we need to scroll the window back in place.
|
|
// This is needed to handle the case where the scrollbar is
|
|
// moved all the way to the right for example, and controls
|
|
// at the left side disappear from the view. Then the user
|
|
// resizes the window wider until scrollbars disappear. Without
|
|
// this code below, the controls off the page will be gone forever.
|
|
if ( deltaPos.cx != 0 || deltaPos.cy != 0 )
|
|
{
|
|
m_attachWnd->ScrollWindow(deltaPos.cx, deltaPos.cy);
|
|
}
|
|
}
|
|
|
|
void CScrollHelper::UpdateScrollBar(int bar, int windowSize, int displaySize,
|
|
LONG& pageSize, LONG& scrollPos, LONG& deltaPos)
|
|
{
|
|
int scrollMax = 0;
|
|
deltaPos = 0;
|
|
if ( windowSize < displaySize )
|
|
{
|
|
scrollMax = displaySize - 1;
|
|
if ( pageSize > 0 && scrollPos > 0 )
|
|
{
|
|
// Adjust the scroll position when the window size is changed.
|
|
scrollPos = (LONG)(1.0 * scrollPos * windowSize / pageSize);
|
|
}
|
|
pageSize = windowSize;
|
|
scrollPos = min(scrollPos, displaySize - pageSize);
|
|
deltaPos = m_attachWnd->GetScrollPos(bar) - scrollPos;
|
|
}
|
|
else
|
|
{
|
|
// Force the scrollbar to go away.
|
|
pageSize = 0;
|
|
scrollPos = 0;
|
|
deltaPos = m_attachWnd->GetScrollPos(bar);
|
|
}
|
|
|
|
SCROLLINFO si;
|
|
memset(&si, 0, sizeof(SCROLLINFO));
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.fMask = SIF_ALL; // SIF_ALL = SIF_PAGE | SIF_RANGE | SIF_POS;
|
|
si.nMin = 0;
|
|
si.nMax = scrollMax;
|
|
si.nPage = pageSize;
|
|
si.nPos = scrollPos;
|
|
m_attachWnd->SetScrollInfo(bar, &si, TRUE);
|
|
}
|
|
|
|
// Helper function to get client rect with possible
|
|
// modification by adding scrollbar width/height.
|
|
void CScrollHelper::GetClientRectSB(CWnd* pWnd, CRect& rect)
|
|
{
|
|
ASSERT(pWnd != NULL);
|
|
|
|
//CRect winRect;
|
|
//pWnd->GetWindowRect(&winRect);
|
|
//pWnd->ScreenToClient(&winRect);
|
|
|
|
pWnd->GetClientRect(&rect);
|
|
|
|
int cxSB = ::GetSystemMetrics(SM_CXVSCROLL);
|
|
int cySB = ::GetSystemMetrics(SM_CYHSCROLL);
|
|
|
|
int style = pWnd->GetStyle();
|
|
if (style & WS_HSCROLL)
|
|
{
|
|
rect.bottom += cySB;
|
|
}
|
|
|
|
if (style & WS_VSCROLL)
|
|
{
|
|
rect.right += cxSB;
|
|
}
|
|
|
|
if (rect.Width() < m_displaySize.cx)
|
|
{
|
|
rect.bottom -= cySB;
|
|
}
|
|
|
|
if (rect.Height() < m_displaySize.cy)
|
|
{
|
|
rect.right -= cxSB;
|
|
|
|
if (rect.Width() < m_displaySize.cx)
|
|
{
|
|
rect.bottom -= cySB;
|
|
}
|
|
}
|
|
}
|
|
// END
|
|
|