Introduction
I just tried Mark Findlay's Scrolling Credits Dialog and Darren S. Gonzales' Scrolling Screen Credits, and I was honestly disappointed. You could do no formatting at all except to choose between 4 different pre-defined text headings (in Darren's version), and it didn't meet the requirements I would make to an "advanced" about-box, where you would usually want to put a hyperlink or two, and maybe Easter-eggs, who knows :)
And as it, IMHO, is lame to say (and think, for that matter) something sux if you can't do it better, I started to code..
Anyway, this control allows you to display formatted scrolling text & images, which would normally be used in an about box for displaying credits. The features include:
- Extended formatting capabilities;
- Supports inline resource bitmaps;
- You can define text and images as hyperlinks which can trigger a
ShellExecute
command or a user-defined callback function;
- Text font, size, style and color as well as image border-color can be changed when mouse-pointer is moved over a hyperlink;
- You can make fancy owner-drawn background effects;
- Supports text and bitmap transparency;
- Doesn't scroll if the total size of the content is smaller than the control;
- Ability to choose whether the user should be able to manually scroll through the content;
- Sorry, not UNICODE compliant (I have no means of testing this.. my version of VC++ (Introductory edition :( ) hasn't even got the libraries required to compile it in Unicode mode... If anybody wants to do this, they are welcome ;) )
Screenshots:
Styles...
.. demo app with animated, user drawn background ..
...OK, this is just a demonstration of some of the functionalities.. I didn't say your about-box had to look that weird.
Usage
To use the Credits control, follow these four easy steps:
- First, you need to add a variable of type
CCreditsCtrl
to your dialog or window class. Don't forget to #include
the required header file.
- Next, call either
SetDataString
or FormatDataString
to set the content of the control.
- If you want to customize the behavior and look of the control, have a look at the various class members. Normally, you would call/set them before calling
Create
.
- Call the
"#create">Create
function.
Construction
Use one of these functions to create the CCreditsCtrl
control:
BOOL Create(DWORD dwExStyle, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd,
UINT nID = 0, UINT nDefaultCursorID = 0,
UINT nLinkCursorID = 0);
BOOL Create(DWORD dwExStyle, DWORD dwStyle,
UINT nPlaceholderID, CWnd* pParentWnd,
UINT nID = 0, UINT nDefaultCursorID = 0,
UINT nLinkCursorID = 0);
These functions create the control using the styles and positions you specify:
dwExStyle
defines the extended styles.
dwStyle
defines the control's window styles.
pParentWnd
is the parent window.
nID
is the control's child ID.
nDefaultCursorID
is the ID of a cursor resource or 0 for default.
nLinkCursorID
is the ID of a cursor resource that will be used when the mouse is over a link.. 0 for default.
rect
is the rectangle of the control.
nPlaceholderID
is the ID of a child window whose position will be used to create the window. This one is used in the demo.
Content
You set the content using one of these four functions:
CString SetDataString(LPCTSTR lpszNewString);
CString SetDataString(UINT nStringResourceID);
CString FormatDataString(LPCTSTR lpszFormat, ...);
CString FormatDataString(UINT nFormatID, ...);
All of them return the old content string. SetDataString
sets the internal data string to the content of lpszNewString
or to the content of the resource string specified by nStringResourceID
. FormatDataString
does the same to the internal data string that CString::Format
does to a CString
...
Calling FormatDataString(... , ...)
does the same that the following piece of code would do:
CString s;
s.Format(... , ...);
m_wndCreditsCtrl.SetDataString(s);
For information about what the content is supposed to look like, please refer to the language section.
Background
You have two options for drawing the background of the control:
- If you just want a single-colored background, you can let
CCreditsCtrl
draw it; you just have to specify the color using the "#setdefaultbkcolor">SetDefaultBkColor
function.
- The other option is to draw the background yourself by specifying a callback function that should do the job.
The function should be defined like this:
static void DrawCreditsBackground(CDC *pDC,
RECT rect, BOOL bAnimate, DWORD lParam);
pDC
is a pointer to the device context to draw to. It's a memory device context, so you don't have to do more BitBlt
's than necessary.
rect
is the client rectangle of the Credits control.
bAnimate
is a flag that tells if the background is supposed to be animated or not... you don't have to respect that, but you might get unexpected results... but go ahead and try without, first :) ..or look at the demo to see how I used it.
lParam
can be anything you want; look below.
Assuming that DrawCreditsBackground
is a member of a class called CAboutDlg
, and that m_wndCredits
is a variable of type CCreditsCtrl
, you would then use the following lines of code to override the default background drawer:
...
m_wndCredits.m_pBackgroundPaint = CAboutDlg::DrawCreditsBackground;
m_wndCredits.m_dwBackgroundPaintLParam = <code>dwAnything;
...
m_wndCredits.Create(...);
..where dwAnything
is the value you want CAboutDlg::DrawCreditsBackground
to receive as lParam
.
"Language"
OK, this is not really a language.. it's a few markup tags that tells the integrated parser, what the formatting should look like. It could be defined as custom HTML with XML rules... the perfect mix for your next headache :)
OK, here we go... first some basic guidelines:
- Newline characters are not stripped, however it is recommended that you use the
<br>
and <p>
tags (see below)... who knows maybe there will be a next version.
- Unlike in HMTL, whitespace stays.
- Again, unlike in HTML, tag parameters must be surrounded by either single- or double quotes:
<tag param='value '>
or <tag param=" value">
. This is important!
- Never, ever forget closing tags when required! (see reference below)
- The parser isn't fault tolerant... which means that if you make formatting errors, it could make your program bug.. or what is worse! (However, you are still allowed to make spelin mistaks :)
Now to the tags:
- font
This is the most important one. It allows you to specify the font face, size, style, color and background as well as horizontal and vertical alignment for both text and images.
It is used like this:
<font face='FontFace' size='n' style=' | [i|-i] | [u|-u] | [s|-s]' color='n,n,n' background='red,green,blue' align='left|center|right' valign='top|middle|bottom'></font>
Looks confusing? Read on.
Parameters:
face
Specifies the font-name
size
The font-size in device units
style
b
means bold, i
means italic, u
means underline, s
means strikeout. You can combine any of those. If you precede one or more of the characters with a minus (-), it removes the corresponding style or styles (when using nested tags).
color
The color of the font. The values can be from 0 to 255.
background
Text background color. Same format as color
.
align
Horizontal alignment of the current line. Only the alignment in the last open font
tag (with the align
parameter specified) before a line break, will be considered: (all of this text will we centered :) ) <font align='left'>this <font align='center'>is a <font align='right'>test</font><br></font></font>
valign
Defines how the current block is aligned on the line. E.g., if you have a line with a 50 pixel high bitmap and some 12 unit high text beside it, valign
would tell the parser if the text should be aligned at the top, at the middle or at the bottom of the bitmap. Hope you got it, otherwise take a look at the demo.
Notes & Tips:
- This tag must always be combined with a closing
</font>
tag. Just like in HTML, except that, here things could get worse if you forgot it.
- This tag can be nested, which means that you can put multiple
font
tags inside each other. A nested font
tag keeps the attributes of the parent tag and changes only the specified parameters.
- You would also use the
font
tag to change the alignment of images.
Example:
<font face='arial' size="14" color='255,0,0' style='buis'> <font style='-i-s'>some text</font></font>
In this example, "some text" would be rendered with an Arial font, of size 14 in red, and would be bolded and underlined. Note that the second tag removes the italic and strikeout styles defined in the first.
- img
This tag allows you to insert an inline image:
<img href='#n' border='n' color='n,n,n'>
Parameters
src
This parameter must be a hash character (#) followed by the numerical resource ID of the bitmap (in text-form, see the notes).
border
Specifies the border-width. Can be zero.
color
The border-color. Can be none
for transparent.
Notes & Tips:
An easy way to specify the src
parameter would be to do something similar to this:
m_wndMyCreditsCtrl.FormatDataString("<img src='#%d'>",nBitmapID);
If you use none
for the color
parameter, the value of border
defines the space between the image and the element surrounding it.
If whatever is behind the pound sign in the src
parameter isn't a valid bitmap-resource-ID, you'll get an assertion! Be careful here.
- a
This tag is for defining text blocks and images as hyperlinks...
<a href='TextToExecute | #[UserArgument#]CallbackFunction' face='FontFace' size='n' style=' | [i|-i] | [u|-u] | [s|-s]' color='n,n,n' background='red,green,blue' align='left|center|right' valign='top|middle|bottom'></a>
Parameters
href
What the link does. You have two options here: Either you can specify anything that will be passed as the command parameter to ShellExecute
or you can specify your own callback function by setting the first character to a hash sign.
Let's have an example:
class CAboutDlg : public CDialog
{
...
CCreditsCtrl m_wndCreditsCtrl;
...
static void Test(LPCTSTR lpszArg)
{ AfxMessageBox(lpszArg); }
...
afx_msg void OnInitDialog()
{
...
m_wndCreditsCtrl.FormatDataString(
"<a href='#Hello World!#%d'>Click Me!</a>",
(long)CAboutDlg::Test);
m_wndCreditsCtrl.Create(...);
...
}
}
This would display a CCreditsCtrl
containing a link with the text "Click Me!". If you clicked that text, it would pop up a message box saying "Hello World!". Note that if you don't specify an argument (e.g. <a href='#%d'>
), lpszArg
is NULL
.
Important Note: Don't even try to specify an invalid address here! The rest of the parameters are the same as in <font>
except that they only take effect when the cursor is over the link. If there is an image inside the <a>
tag, the color attribute defines the border color when the cursor is over the image.
Notes & Tips:
You can achieve a cool effect for images if you use a border-width of 1 or 2 in the image
tag with a border-color set to none
and some other color in the <a>
tag. This will make the border appear only when the mouse is over the image.
You can open a browser window and make it navigate to a specific URL by setting href
to e.g. "http://www.yourwebpage.com".
You can start the user's mail application by setting href
to mailto:foo@foo.org
or mailto:foo@foo.org?SUBJECT=Hello World!
", if you want a predefined subject. <a>
tags can not be nested. Don't forget the closing of </a>
tag!
- br
Inserts a line-break. Usage: <br>
No Comments.
Oh, yeah.. just one comment: If the line before the br
tag is empty (no text or images), the line break gets the height of the current font. This is also true for <p>
(see below).
- p
Inserts two line-breaks. Usage: <p>
The height of the space equals the height of the current font.
- hspace
Inserts a horizontal space.
<hspace size='n'>
Parameters
Notes & Tips:
A space can not be a hyperlink.
- vspace
Wraps the current line if it isn't empty and inserts a vertical space.
<vspace size='n'>
Parameters
Notes & Tips:
A space can (still) not be a hyperlink
That's it!! -- No more tags :-D
If you experience problems with this "language", have a look at the demo or at the source of the CCreditsCtrl::Initialize()
function (not recommended). And if you still can't solve a problem or think it's a bug, post it below.
...ENJOY!!
Misc. Class Members
Here I will briefly comment some useful public class members, in case you'll ever need them...
BOOL Create(...); |
For a detailed description, see construction |
CString SetDataString(...); |
Sets the content of the control.
Return value: old content.
For more information, see content. |
CString FormatDataString(...); |
Sets the content of the control.
Return value: old content
For more information, see content. |
CString GetDataString(); |
Returns the value of m_sData which contains the content of the control. |
int HitTest(CPoint pt); |
Returns the index of the link under the cursor. Can be used with m_HotRects and m_HotRectActions. If the cursor isn't over a link, -1 is returned |
void SetDefaultBkColor(COLORREF crColor); |
Sets the background color that the default background drawer will use. If you use your own background drawer, don't call this function! |
void Initialize(); |
Parses the content of m_sData and fills m_HotRects and m_HotRectActions. If you modify m_sData manually (e.g. without calling SetDataString or FormatDataString ), call this function to update your changes. |
void SetDefaultLinkCursor(); |
Sets the link cursor to a hand-cursor. I got this one from Chris Maunder's Hyperlink Control - thanks! |
-- variables -- |
BOOL m_bCanScroll |
TRUE if the user should be allowed to manually scroll through the content of the control. |
int m_nCurBitmapOffset |
Current scroll-offset... normally you wouldn't want to modify this one. |
int m_nTimerSpeed |
Speed used in SetTimer() . Change this to get a slower/faster scroll speed. Default: 40 |
HCURSOR m_hDefaultCursor |
Handle to the default cursor. If you don't want to specify it with a resource ID in the "#construction">Create function, you can change this variable.. after having called Create . |
HCURSOR m_hLinkCursor |
Same as m_hDefaultCursor but only used when the cursor is over a link. |
CString m_sData |
String containing the content of the control. Should be set with SetDataString or FormatDataString. |
CArray<CRect,CRect&> m_HotRects |
An array containing the rectangles of all the links relative to the top of the content-bitmap (not the client rectangle). You should not modify its content. |
CArray<CString,CString&> m_HotRectActions |
Array containing all the link actions. Index matches the ones from m_HotRects . Should not be modified. |
COLORREF m_crInternalTransparentColor |
Color used internally as transparent color. Everything drawn with this color will be transparent (including bitmaps and text). You can use this to add transparency effects to your images.
Also this is the color fonts are initialized to (unless you specify a text background), so choose a color as close to the actual background color as possible (e.g., if you have a dark background, you would choose black as transparent color, or RGB(1,1,1) if you don't want the black parts of your bitmaps to be transparent.) |
void(*m_pBackgroundPaint)(CDC*,RECT,BOOL,DWORD) |
This variable holds the pointer of the callback function used for background drawing.
Please refer to the background section for more information. |
DWORD m_dwBackgroundPaintLParam |
Value that the function stored in m_pBackgroundPaint will receive as its fourth parameter (lParam ) |
Tips and Tricks
- Download the Demo!
- RTFM!
- Customize
"#m_crInternalTransparentColor">m_crInternalTransparentColor
to fit your needs.
- Most color parameters (if not all) can take a value of
none
or transparent
which will make them... transparent!
- The control begins scrolling
(m_nTimerSpeed*10)
milliseconds after it has been created. If you want to change this delay, modify m_nTimerSpeed
just before calling Create
and modify it back just after. For example, if you want to completely remove the delay, you would do like this: ...
int nOldSpeed = m_wndCreditsCtrl.m_nTimerSpeed;
m_wndCreditsCtrl.m_nTimerSpeed = 0;
m_wndCreditsCtrl.Create(...);
m_wndCreditsCtrl.m_nTimerSpeed = nOldSpeed;
...
- Add a good amount of
"#vspace"><vspace>
at the end of the content... otherwise the beginning of the content will be displayed immediately after the end, and this is usually not what you want.
- A computer program does what you tell it to do, not what you want it to do... these are wise words :)