在窗口上写字
以前写过一个类似的:http://dev.youkuaiyun.com/develop/article/29/29193.shtm
不过没有处理好back space等问题。而且显示字体也收到限制。这个依然是个不成熟的,不过可以改进。
类代码:
==============================================================
---TextFun.h---
/************************************************************
* Note:
* A class of drawing font on any window witch was attached to
* an instance of this class.
*
* Bugs to fit:
* 1, No memory dc supported so that XORPEN maybe disturb the
* existing font shape on dc.
* 2, The class cannot serialize the drawing opts.
* 3, On small font, the urgly face to be improved.
*
* By enoloo
* First edition : 8/10,2004
************************************************************/
#ifndef _TextFun_H_
#define _TextFun_H_
#include <string>
#include <tchar.h>
#include <windows.h>
#include <assert.h>
class CTextFun
{
public:
CTextFun(){}
CTextFun(HWND hWnd,HFONT hFont = NULL,
COLORREF color = RGB(0,0,255));
~CTextFun();
public:
void Attach(HWND hWnd,HFONT hFont = NULL,
COLORREF color = RGB(0,0,255));
void Enter(POINT point);
void Leave();
bool IsAttached() const { return (m_hWnd != NULL && ::IsWindow(m_hWnd)); }
public:
bool PushChar(UINT nChar,bool bForward);
void PopChar();
void PushString(LPCTSTR str2Push);
std::string GetString() const { return m_strText; }
long GetHeight() const { return m_nHeight; }
long GetWidth() const { return (m_ptNow.x - m_ptStart.x); }
POINT GetCurrentPoint() const { return m_ptNow; }
protected:
// create a default TRUE-TYPE font here.
void CreateDefaultFont();
void _PushString(LPCTSTR str2Push,bool bForward);
protected:
HWND m_hWnd;
HFONT m_hFont;
std::string m_strText;
POINT m_ptStart;
POINT m_ptNow;
COLORREF m_clColor;
long m_nHeight;
bool m_bDefaultFont;
};
#endif
========================================================
---TextFun.cpp---
#include "TextFun.h"
CTextFun::CTextFun(HWND hWnd, HFONT hFont /* = NULL */,
COLORREF color /* = RGB */)
{
Attach(hWnd,hFont,color);
}
CTextFun::~CTextFun()
{
if(m_bDefaultFont)
::DeleteObject(m_hFont);
Leave();
}
// NOTE: hFont must be a true type font.
void CTextFun::Attach(HWND hWnd,HFONT hFont /* = NULL */, COLORREF color /* = RGB */)
{
assert(hWnd);
assert(::IsWindow(hWnd));
m_hWnd = hWnd;
HDC hdc = NULL;
hdc = ::GetDC(hWnd);
assert(hdc);
if(hFont == NULL)
{
m_bDefaultFont = true;
/*
HFONT hft = (HFONT)::GetCurrentObject(hdc, OBJ_FONT);
assert(hft);
m_hFont = hft;
*/
CreateDefaultFont();
}
else
{
m_bDefaultFont = false;
m_hFont = hFont;
}
m_clColor = color; //color;
HFONT oldFt = (HFONT)::SelectObject(hdc,m_hFont);
TEXTMETRIC tm;
::GetTextMetrics(hdc, &tm);
m_nHeight = tm.tmHeight;
::SelectObject(hdc,oldFt);
::ReleaseDC(hWnd,hdc);
}
void CTextFun::Enter(POINT point)
{
m_ptStart = m_ptNow = point;
::DestroyCaret();
assert( ::CreateCaret(m_hWnd, NULL, 1, m_nHeight) );
::ShowCaret(m_hWnd);
::SetCaretPos(point.x, point.y);
}
void CTextFun::Leave()
{
m_ptStart.x = m_ptNow.x = m_ptStart.y = m_ptNow.y = 0;
m_strText.erase();
::HideCaret(m_hWnd);
::DestroyCaret();
}
// for OnChar...
bool CTextFun::PushChar(UINT nChar,bool bForward)
{
static bool bFirstChinese = false;
static char strTemp[3];
if(nChar > 127 && !bFirstChinese) // maybe a chinese char code
{
if(bForward)
strTemp[0] = nChar;
else
strTemp[1] = nChar;
bFirstChinese = true;
return false; // wait for the second char code
}
else if(nChar > 127 && bFirstChinese)
{
if(bForward)
strTemp[1] = nChar;
else
strTemp[0] = nChar;
strTemp[2] = 0;
bFirstChinese = false;
}
else // an ascii char code
{
bFirstChinese = false; // back the status here.
strTemp[0] = nChar;
strTemp[1] = 0;
}
_PushString(strTemp,bForward);
return true;
}
void CTextFun::PopChar()
{
int strLength = m_strText.length();
if(m_strText.empty())
return;
HDC hdc = NULL;
hdc = ::GetDC(m_hWnd);
assert(hdc);
if(!PushChar( m_strText[strLength - 1], false )) // maybe a chinese char code
{
if(m_strText.empty())
{
m_strText.erase();
return;
}
PushChar( m_strText[strLength - 2], false );
}
return;
}
void CTextFun::_PushString(LPCTSTR str2Push,bool bForward)
{
// Create a caret always.
int strLen = _tcsclen(str2Push);
assert (strLen >= 0);
assert( ::CreateCaret(m_hWnd, NULL, 1, m_nHeight) );
::ShowCaret(m_hWnd);
HDC hdc = NULL;
hdc = ::GetDC(m_hWnd);
assert(hdc);
int preX = m_ptNow.x; // save it.
LOGBRUSH logBr;
logBr.lbColor = m_clColor;
logBr.lbHatch = 0;
logBr.lbStyle = BS_SOLID;
HPEN pen,oldPen;
pen = (HPEN)::ExtCreatePen( PS_GEOMETRIC | PS_SOLID |
PS_ENDCAP_SQUARE | PS_JOIN_BEVEL, 1, &logBr, 0, NULL);
// pen = (HPEN)::CreatePen(PS_SOLID,1,m_clColor);
oldPen = (HPEN)::SelectObject(hdc, (HGDIOBJ)pen);
HBRUSH br,oldBr;
br = ::CreateSolidBrush(m_clColor);
oldBr = (HBRUSH)::SelectObject(hdc, (HGDIOBJ)br);
HFONT oldFt = (HFONT)::SelectObject(hdc,m_hFont);
RECT rtQuery;
::DrawText(hdc, str2Push, _tcslen(str2Push), &rtQuery, DT_CALCRECT); // get the RECT for the char to draw
HRGN hRgn;
if(bForward)
{
m_ptNow.x = m_ptNow.x + (rtQuery.right - rtQuery.left);
hRgn = ::CreateRectRgn(preX, m_ptNow.y, m_ptNow.x, m_ptNow.y + m_nHeight);
}
else
{
m_ptNow.x = m_ptNow.x - (rtQuery.right - rtQuery.left);
hRgn = ::CreateRectRgn(m_ptNow.x, m_ptNow.y, preX, m_ptNow.y + m_nHeight);
}
// ::ExtSelectClipRgn(hdc,hRgn,RGN_COPY);
// set the caret's pos now
::SetCaretPos(m_ptNow.x, m_ptNow.y);
::SetBkMode(hdc,TRANSPARENT);
// draw the char code
// ::SetPolyFillMode(hdc,ALTERNATE);
int preR2 = ::SetROP2(hdc,R2_NOTXORPEN);
::BeginPath(hdc);
if(bForward)
::TextOut(hdc, preX, m_ptNow.y, str2Push , _tcslen(str2Push));
else
::TextOut(hdc, m_ptNow.x, m_ptNow.y, str2Push, _tcslen(str2Push));
::EndPath(hdc);
// NOTE: DONT call a silly function StrokeAndFillPath!
::StrokeAndFillPath(hdc);
// ::SelectClipPath(hdc,RGN_AND);
::SetROP2(hdc,preR2);
::SelectObject(hdc, (HGDIOBJ)oldPen);
::SelectObject(hdc, (HGDIOBJ)oldBr);
::SelectObject(hdc, (HGDIOBJ)oldFt);
// save the char code
if(bForward)
m_strText += std::string(str2Push);
else
m_strText = m_strText.substr(0, m_strText.length() - _tcslen(str2Push));
return;
}
void CTextFun::PushString(LPCTSTR str2Push)
{
_PushString(str2Push,true);
}
void CTextFun::CreateDefaultFont()
{
LOGFONT lf;
::memset(&lf,0,sizeof(LOGFONT));
lf.lfHeight = 40;
lf.lfWeight = /*FW_BOLD*/ FW_NORMAL;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
lf.lfItalic = FALSE;
lf.lfUnderline = FALSE;
lf.lfStrikeOut = FALSE;
lf.lfCharSet = ANSI_CHARSET;
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
lf.lfClipPrecision = DRAFT_QUALITY;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_MODERN;
strcpy(lf.lfFaceName, "Arial");
m_hFont = ::CreateFontIndirect(&lf);
}
说明:
类支持在一个和类对象绑定(Attach)的窗口上写字,支持所有true-type字体。支持bake escape键。如果换行,可以从类中得到高度,在下面创建一个新的类对象就可以了。
不支持上下左右光标,没有memdc的支持,因为是用NotXorPen和路径作出来的,所以文字会在叠加的时候产生干扰。 解决这些问题应该不是很困难。
使用:
1, 将窗体和CTextFun对象绑定。绑定中可以指定true-type字体和颜色。也可以使用默认的。m_fun为一个CTextFun对象,调用:
m_fun.Attach(this->GetSafeHwnd());
2,显示文字可以调用Enter和Leave来完成。Enter函数设置显示光标的位置,Leave函数清空保存的缓冲文字数据。如果在视图中,可以在OnLButtonDown中调用:
m_fun.Enter(point);
在OnChar中,可以这么调用:
void CChildView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if(nChar == VK_BACK) // back键就退格,和文本框一样
{
m_fun.PopChar();
CWnd::OnChar(nChar,nRepCnt,nFlags);
return;
}
else if(nChar == VK_RETURN) // 回车键,结束显示
{
m_fun.Leave();
CWnd::OnChar(nChar,nRepCnt,nFlags);
return;
}
m_fun.PushChar(nChar,true); // 普通键,再绑定的窗口上显示
CWnd ::OnChar(nChar, nRepCnt, nFlags);
}
3,上面在OnChar中同步显示字符,如果要一次显示一个串呢,可以调用:
m_fun.PushString("中国"); // 比如在OnLButtonDown中调用
效果如下:
测试代码等找到免费空间之后传上来的J。
By enoloo,
8/10,2004