|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionRecently I needed a scroll bar that looked and acted like the one in Windows Media Player:
The closest one I could find was the
CXScrollBar In ActionTo demonstrate new scroll bar, I decided to create an RGB color tool, which would benefit from having colors displayed on scroll bars:
Turning off thumb color also turns off channel color:
You can also turn off channel color by itself:
The thumb gripper can also be disabled:
Using CXScrollBarUse of XScrollBar will usually be in dialogs,
but you can create XScrollBar anywhere by using one
of the two
CreateFromWindow()//============================================================================= // // CreateFromWindow // // Purpose: Create the CXScrollBar control from placeholder window // // Parameters: dwStyle - the scroll bar’s style. Typically this will be // SBS_HORZ|WS_CHILD|SS_LEFT|SS_NOTIFY|WS_VISIBLE. // pParentWnd - the scroll bar’s parent window, usually a CDialog // object. It must not be NULL. // hWnd - HWND of placeholder window (must already exist) // nId - the resource id of the CXScrollBar control // // Returns: BOOL - TRUE = success //
Here is how the // red
VERIFY(m_HorizontalScrollBar1.CreateFromWindow(
SBS_HORZ | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
this, GetDlgItem(IDC_SCROLL1_RECT_HORZ)->m_hWnd, IDC_SCROLL1_HORZ));
After creating the XScrollBar control, the demo app then sets its range, color, and gripper options: m_HorizontalScrollBar1.SetScrollRange(0, 255)
.SetThumbColor(RGB(255,0,0))
.EnableThumbGripper(m_bThumbGripper);
Setting thumb color also has effect of enabling display of thumb color. The channel color is enabled by default, and will be displayed if thumb color is enabled.
CreateFromRect()In case it is not convenient to create a placeholder window, you can also create XScrollBar control by specifying rect: //============================================================================= // // CreateFromRect // // Purpose: Create the CXScrollBar control from rect // // Parameters: dwStyle - the scroll bar’s style. Typically this will be // SBS_HORZ|WS_CHILD|SS_LEFT|SS_NOTIFY|WS_VISIBLE. // pParentWnd - the scroll bar’s parent window, usually a CDialog // object. It must not be NULL. // rect - the size and position of the window, in client // coordinates of pParentWnd // nId - the resource id of the CXScrollBar control // // Returns: BOOL - TRUE = success //
Implementation Notes
XScrollBar Component StructureThe diagram below shows the components of an XScollBar, and the nomenclature that will be used in this article:
As you probably guessed, the XScrollBar is drawn in five steps
(six, if you count the border). The drawing takes place
in
Drawing the XScrollBar: Step 1 - Left Arrow Button
The left arrow button bitmap looks like this (the orange border is not part of the bitmap):
One thing to note is that arrow bitmaps and thumb bitmap are assumed to
be all the same size, which simplifies some calculations. The size of the
thumb bitmap ( Knowing that width of the left arrow bitmap = width of the thumb bitmap, we can write CRect rectLeftArrow(m_rectClient.left, m_rectClient.top,
m_rectClient.left + m_nBitmapWidth, m_rectClient.bottom);
Drawing the XScrollBar: Steps 2 & 3 - ChannelNext we draw channel. In case a color is being displayed in the channel, it must be drawn in two parts. Because we modify the bitmap to add color, we draw the right channel (the part without color) first. The channel bitmap is one pixel wide and looks like this (the orange border is not part of the bitmap):
Earlier, we set the channel position and width from the thumb width: int nChannelStart = m_rectClient.left + m_nBitmapWidth;
int nChannelWidth = m_rectClient.Width() - 2*m_nBitmapWidth;
Now we use these values to define the rect for the right channel: CRect rectChannelRight(m_rectThumb.left + m_nBitmapWidth/2, m_rectClient.top,
nChannelStart + nChannelWidth, m_rectClient.bottom);
If a thumb color and channel color (both are required) are desired,
then we must modify the channel bitmap to add color. But what color to use?
Here is where Christian Rodemeyer's if (m_bChannelColor && m_bThumbColor)
{
COLORREF rgb1, rgb2, rgb3;
GetChannelColors(rgb1, rgb2, rgb3);
BITMAP bm;
bmpChannel.GetBitmap(&bm);
// set highlight colors
bitmapDC.SetPixel(0, 0, rgb1);
bitmapDC.SetPixel(0, 1, rgb2);
// set main color
for (int y = 2; y < (bm.bmHeight); y++)
bitmapDC.SetPixel(0, y, rgb3);
}
and here is //=============================================================================
void CXScrollBar::GetChannelColors(COLORREF& rgb1,
COLORREF& rgb2,
COLORREF& rgb3)
//=============================================================================
{
CColor color;
color.SetRGB(GetRValue(m_ThumbColor),
GetGValue(m_ThumbColor),
GetBValue(m_ThumbColor));
color.ToHLS();
float fLuminance = color.GetLuminance();
// use 80% L, 50% S for main color
fLuminance = 0.8f;
float fSaturation = color.GetSaturation();
fSaturation = 0.5f * fSaturation;
float fHue = color.GetHue();
color.SetHLS(fHue, fLuminance, fSaturation);
color.ToRGB();
rgb3 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
// use .87 L for second highlight color
fLuminance = .87f;
color.SetHLS(fHue, fLuminance, fSaturation);
color.ToRGB();
rgb2 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
// use .92 L for first highlight color
fLuminance = .92f;
color.SetHLS(fHue, fLuminance, fSaturation);
color.ToRGB();
rgb1 = RGB(color.GetRed(), color.GetGreen(), color.GetBlue());
}
We can now define the rect for the left channel: CRect rectChannelLeft(nChannelStart, m_rectClient.top,
m_rectThumb.left + m_nBitmapWidth/2, m_rectClient.bottom);
Drawing the XScrollBar: Step 4 - Right Arrow Button
The right arrow button bitmap looks like this (the orange border is not part of the bitmap):
The right arrow button is drawn much like the left arrow button.
Drawing the XScrollBar: Step 5 - Thumb
The thumb bitmap (for a thumb with no color) looks like this:
For a thumb with color it looks like:
The thumb bitmaps are modified before being drawn to the memory DC. For a thumb with no color, the only modification that may be necessary is to replace the thumb gripper pixels with pixels taken from the surrounding area on the thumb, thus making the gripper disappear (how this is done will be explained below). For a thumb with color, in addition to the gripper pixels, it is also necessary to make the corner pixels transparent (i.e., same color as channel). and to add the selected thumb color (or hover color). All these changes are accomplished by using four "special" RGB color values:
These colors are used in the thumb bitmaps as shown here:
Here is the code that performs these pixel replacements: CClientDC dc(this);
CDC bitmapDC;
bitmapDC.CreateCompatibleDC(&dc);
CBitmap *pOldBitmap = bitmapDC.SelectObject(&m_bmpThumbHot);
// add desired hot color to thumb
ColorThumb(&bitmapDC, m_ThumbHoverColor);
bitmapDC.SelectObject(&m_bmpThumb);
// add desired cold color to thumb
ColorThumb(&bitmapDC, m_ThumbColor);
and here is //=============================================================================
void CXScrollBar::ColorThumb(CDC *pDC, COLORREF rgbThumb)
//=============================================================================
{
COLORREF rgbPrev = 0;
// add desired hot color to thumb
for (int x = 0; x < m_nBitmapWidth; x++)
{
for (int y = 0; y < m_nBitmapHeight; y++)
{
COLORREF rgb = pDC->GetPixel(x, y);
if (m_bThumbColor && (rgb == THUMB_MASK_COLOR))
{
pDC->SetPixel(x, y, rgbThumb);
}
else if (rgb == THUMB_GRIPPER_MASK_COLOR)
{
if (m_bThumbGripper)
pDC->SetPixel(x, y, THUMB_GRIPPER_COLOR);
else
pDC->SetPixel(x, y, rgbPrev);
}
rgbPrev = rgb;
}
}
}
This technique for coloring the thumb and channel works for True Color
(24-bit). Lower resolutions may require you to use other RGB values
for the four "special" RGB thumb colors, and it may also require
that you limit the color used in the channel.
Mouse Hover
A final thing to take care of is when the mouse hovers over the thumb. In this case, the thumb color will be changed to the hover color (as seen in the above code), and the cursor will be changed to a hand:
This is handled in the
How To UseTo integrate XScrollBar into your app, you first need to add following files to your project:
Next copy the bitmap files to the project's res directory, and add the bitmaps to the .rc file. I find it easiest just to edit the .rc file manually: ///////////////////////////////////////////////////////////////////////////// // // Bitmap // IDB_HORIZONTAL_SCROLLBAR_LEFTARROW BITMAP DISCARDABLE "res\\HorizontalScrollBarLeftArrow.bmp" IDB_HORIZONTAL_SCROLLBAR_RIGHTARROW BITMAP DISCARDABLE "res\\HorizontalScrollBarRightArrow.bmp" IDB_HORIZONTAL_SCROLLBAR_CHANNEL BITMAP DISCARDABLE "res\\HorizontalScrollBarChannel.bmp" IDB_HORIZONTAL_SCROLLBAR_THUMB BITMAP DISCARDABLE "res\\HorizontalScrollBarThumb.bmp" IDB_HORIZONTAL_SCROLLBAR_THUMB_NO_COLOR BITMAP DISCARDABLE "res\\HorizontalScrollBarThumbNoColor.bmp" IDB_VERTICAL_SCROLLBAR_UPARROW BITMAP DISCARDABLE "res\\VerticalScrollBarUpArrow.bmp" IDB_VERTICAL_SCROLLBAR_DOWNARROW BITMAP DISCARDABLE "res\\VerticalScrollBarDownArrow.bmp" IDB_VERTICAL_SCROLLBAR_CHANNEL BITMAP DISCARDABLE "res\\VerticalScrollBarChannel.bmp" IDB_VERTICAL_SCROLLBAR_THUMB BITMAP DISCARDABLE "res\\VerticalScrollBarThumb.bmp" IDB_VERTICAL_SCROLLBAR_THUMB_NO_COLOR BITMAP DISCARDABLE "res\\VerticalScrollBarThumbNoColor.bmp"And add the bitmap IDs to resource.h: #define IDB_HORIZONTAL_SCROLLBAR_LEFTARROW 201 #define IDB_HORIZONTAL_SCROLLBAR_RIGHTARROW 202 #define IDB_HORIZONTAL_SCROLLBAR_CHANNEL 203 #define IDB_HORIZONTAL_SCROLLBAR_THUMB 204 #define IDB_HORIZONTAL_SCROLLBAR_THUMB_NO_COLOR 205 #define IDB_VERTICAL_SCROLLBAR_UPARROW 206 #define IDB_VERTICAL_SCROLLBAR_DOWNARROW 207 #define IDB_VERTICAL_SCROLLBAR_CHANNEL 208 #define IDB_VERTICAL_SCROLLBAR_THUMB 209 #define IDB_VERTICAL_SCROLLBAR_THUMB_NO_COLOR 210 Of course, you should verify the numerical IDs are unique.
Future Enhancements
Acknowledgments
Revision History
Version 1.2 - 2008 August 29
Version 1.1 - 2004 September 21
Version 1.0 - 2004 September 9
UsageThis software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause. | ||||||||||||||||||||||||||||||