当我发现,添加文件,计时器,刷新列表出现问题后,我又翻出屋顶打字通代码做了一番修改,勉强添加了显示英的样式,把刷新列表的问题用绝对路径解决了,至此,我的屋顶打字通算是完成了它的第二个版本,这份代码也没什么特别的,就开源出来分享给大家,整个工程几乎用的都是Unicode,当然也有用的不是宽字节的,我写了这两个之间的转换函数,只是字符串之间的转换,这次写打字通学到了,GDI绘图,多线程的使用,U码和A码转换,绝对路径和相对路径的使用,WINDOWS消息机制(主要是绘图)。
用到两种创建窗口的方式,而且这两种方式都通过resource添加了对话框:
DialogBox(hInstance,MAKEINTRESOURCE(ID_MENU_STARTMENU),NULL,DlgMain);//这种方式不会立即返回,只有对话框结束了才会返回
CreateDialog(hIstanceMain,MAKEINTRESOURCE(ID_DIG_GAMEVIEW),NULL,DlaGame);//这种方式会立即返回
设计界面:
一、开始界面
菜单栏,显示列表,选中提示框,开始按钮
(1):菜单栏
(1.1)编辑:
(1.1.1)添加:
(1.1.2)中英文显示转换:
(1.2)帮助:
(1.2.1)链接:
(1.2.2)关于:
(2):显示列表
用于显示文件夹内的TXT文件
(2.1):左键单击选中列表一行,选中提示框内容变化
(2.2):左键双击列表中的一行,直接打开文件
(2.3):右键单击列表中的一行,可以对文件进行操作,包括删除该文件和全删除
(3):选中提示框
用于提示用户选中了哪一行
(4):开始按钮
点击开始按钮则会判断选中框内是否有内容,有,则打开指定文件,没有,则提示用户进行选择
二、打字界面
当前文章标题,进度条,计时器,三行文字显示和输入框,速度显示,错误字数
(1):当前文章标题
显示打开文件的标题
(2):进度条
显示打字进度,数字显示百分比,进度条根据百分比显示相应长度
(3):计时器
当有字符键入时,开始计时
(4):三行显示和输入
(4.1):显示文章的20个字符,根据二维数组内的数字判断画出字的颜色
(4.2):输入框等待用户输入,对输入的每一个字符进行判断,与上面字符相匹配则显示蓝色,不匹配则显示红色,用户不可以更改输入框的焦点,特殊处理有:显示的时候遇到换行符则变成空格,到键入最后一个字符时,如果键入多个的时候,自动换行或换页显示
(4.3):输入至文章末尾,则结束打字界面
(5):速度显示
计时器线程的创建,显示 时 分 秒
(6):错误字数
每次键入都会判断是否匹配,计数显示
打字通写得一般,代码质量也不高,因为这份代码确实是想到哪里写到哪里的,连有那些功能都是想到了才去完成那些部分的,展示给大家全部代码,但是大家也用不了,因为资源文件是我手动创建的,所以直接复制源码还是用不了的,这是WIN32程序,对C/C++感兴趣并且对Windows消息机制和GDI绘图感兴趣的可以仔细看看我这份代码。
TypeMain.cpp
#include"Constant.h"
void EditFontController(float AimSize) {
LOGFONT LogFont;
memset(&LogFont, 0, sizeof(LOGFONT));
lstrcpy(LogFont.lfFaceName, L"Arial");
LogFont.lfWeight = FW_BLACK;//FW_NORMAL;
LogFont.lfHeight = (LONG)AimSize; // 字体大小
LogFont.lfCharSet = 134;
LogFont.lfOutPrecision = 3;
LogFont.lfClipPrecision = 2;
LogFont.lfOrientation = 45;
LogFont.lfQuality = 1;
LogFont.lfPitchAndFamily = 2;
// 创建字体
HFONT hFont = CreateFontIndirect(&LogFont);
// 取得控件句柄
for (int i = 0; i < 3; i++) {//设置控件字体
hWndEdit[i] = GetDlgItem(hWndGameView, EditInput1 + i);
SendMessage(hWndEdit[i], WM_SETFONT, (WPARAM)hFont, TRUE);
}
}
wchar_t* GetCurrentPath(wchar_t * target) {
LPWSTR PointPath = NULL;
wchar_t *cppoint = 0;
cppoint = new wchar_t[MAX_PATH];
cppoint[0] = 0;
GetModuleFileName(NULL, cppoint, MAX_PATH);//获得绝对路径
(_tcsrchr(cppoint, _T('\')))[1] = 0;//截掉exe文件名
if (target) {//根据需要粘贴指定字符串
wcscat_s(cppoint, MAX_PATH, target);//给路径添加MyResource
}
PointPath = cppoint;
return PointPath;
}
void InitArray() {//初始化数组,赋值1
for (int i = 0; i < 3; i++)
for (int j = 0; j < 20; j++)
ColorFlage[i][j] = 1;
}
void InitTxt() {
InitArray();
SetDlgItemText(hWndGameView, EditInput1, L"");//清空文本框内容
SetDlgItemText(hWndGameView, EditInput2, L"");//
SetDlgItemText(hWndGameView, EditInput3, L"");//
}
void Counter(int &hour, int &minute, int &second) {
if (++second == 60) {//计时器
TimePane[6] = 0;//满60秒记1分钟,秒的十位归零
if (++minute == 60) {
TimePane[3] = 0;//满60分钟记1小时,分钟的十位归零
if (++hour == 60)
return;
minute = 0;
}
second = 0;
}
}
void TimeChanger(int time, int shift) {
int tmp1 = 0;
wchar_t wtmp[2];//必须两个,不然没有办法安放' '
if (time<10) {
_itow_s(time, wtmp, 2, 10);
TimePane[shift] = wtmp[0];
}else {
tmp1 = time / 10;
_itow_s(tmp1, wtmp, 2, 10);
TimePane[shift - 1] = wtmp[0];
tmp1 = time % 10;
_itow_s(tmp1, wtmp, 2, 10);
TimePane[shift] = wtmp[0];
}
}
DWORD WINAPI TimerFunction(LPVOID lpParam) {//计时器线程
int Hour = 0, Minute = 0, Second = 0;
RECT tmpR;
for (int i = 0; i < 8; i++) {
TimePane[i] = 0;//TimePane里面是这样的 00:00:00
}//初始化时间面板数组
TimePane[2] = ':';
TimePane[5] = ':';
tmpR.left = 620L;
tmpR.right = 680L;
tmpR.top = 42L;
tmpR.bottom = 62L;
while (hAndleTimer) {
Sleep(1000);//下面采用最麻烦的计时,然后提取个位和十位数字分别放在要显示的面板上
Counter(Hour, Minute, Second);
if (Hour == 60)
return 0;
TimeChanger(Second, 7);
TimeChanger(Minute, 4);
TimeChanger(Hour, 1);
CountSecond = Hour * 60 * 60 + Minute * 60 + Second;//统计时间用于计算速度
InvalidateRect(hWndGameView, &tmpR, false);
UpdateWindow(hWndGameView);
}
return 0;
}
void StepMoveOn(int model) {
if (model == 1)
FlagBack = true;
else
FlagBack = false;
if (FlagBack) {
MOVE = MOVE - 1;
TxtInfo.MoveAddress = TxtInfo.MoveAddress - 1;
}
int GoDown = (int)INTERVAL*(NUMBEREDIT - 1);
AimRect.left = RECTX + (LONG)(MOVE)*(RECTA);
AimRect.right = AimRect.left + RECTA;
if (AimRect.right >= 234)
AimRect.right = AimRect.right + 3L;
if (AimRect.right >= 294)
AimRect.right = AimRect.right + 5L;
if (AimRect.right >= 512)
AimRect.right = AimRect.right + 5L;
AimRect.top = (((LONG)PointY - (LONG)3) + (LONG)GoDown);
AimRect.bottom = (((LONG)PointY - (LONG)3) + ((LONG)SizeFont + (LONG)4) + (LONG)GoDown);
ColorFlage[NUMBEREDIT - 1][MOVE] = model;
if (EnglishOrChinese) {
InvalidateRect(hWndGameView, &AimRect, false);
UpdateWindow(hWndGameView);
}
else {
InvalidateRect(hWndGameView, 0, false);
UpdateWindow(hWndGameView);
}
if (!FlagBack) {
MOVE = MOVE + 1;
TxtInfo.MoveAddress = TxtInfo.MoveAddress + 1;
}
wchar_t * TmpCurrentPathA = GetCurrentPath(NULL);//exe所在绝对路径
wchar_t SoundName[] = L"1466.wav";
wcscat_s(TmpCurrentPathA, MAX_PATH, SoundName);//给路径添加MyResource
PlaySound(TmpCurrentPathA, 0, SND_FILENAME | SND_ASYNC);
delete[] TmpCurrentPathA;
}
void DealEditCallBack(WPARAM wWord) {
if (TxtInfo.TXTSIZE) {
wchar_t TmpChar = (wchar_t)wWord;
wchar_t TmpSource = TxtInfo.TXTCONTENT[TxtInfo.MoveAddress];
int tmpCount = 0;
if (TmpChar != TmpSource) {//匹配失败
if (wWord == 8) {//退格符
if (MOVE>0) {//退格符有效
StepMoveOn(GreyNUMBER);
}
//退格符无效
}
else {//不是退格符且匹配失败
StepMoveOn(RedNUMBER);
if (CountSecond > 0) {
double SPEED = (double)TxtInfo.MoveAddress / (double)CountSecond * 60;
stringstream tmpus;
string tmpstr;
tmpus << (int)SPEED;
tmpus >> tmpstr;
wchar_t *tmp1 = ConvertLPSTRtoLPWSTR(tmpstr.c_str());
SetDlgItemText(hWndGameView, ViewSpeed, tmp1);
delete[] tmp1;
}
}
}else {//匹配成功
StepMoveOn(BlueNUMBER);
if (CountSecond > 0) {
double SPEED = (double)TxtInfo.MoveAddress / (double)CountSecond * 60;
stringstream tmpus;
string tmpstr;
tmpus << (int)SPEED;
tmpus >> tmpstr;
wchar_t * tmp2 = ConvertLPSTRtoLPWSTR(tmpstr.c_str());
SetDlgItemText(hWndGameView, ViewSpeed, tmp2);
delete[] tmp2;
}
}
if (!hAndleTimer) {//计时器线程句柄
hAndleTimer = CreateThread(0, 0, TimerFunction, 0, 0, 0);//创建计时器线程,返回值为句柄赋值给hAndleTimer
}
for (int i = 0; i < 3; i++)
for (int j = 0; j < 20; j++)
if (ColorFlage[i][j] == 3)
tmpCount++;
stringstream tmpus;
string tmpstr;
tmpus << tmpCount;
tmpus >> tmpstr;//输入错误字数计数
wchar_t * tmp3 = ConvertLPSTRtoLPWSTR(tmpstr.c_str());
SetDlgItemText(hWndGameView, ErCount, tmp3);
delete[] tmp3;
if (MOVE == 20) {//换行
if (NUMBEREDIT == 3) {//当焦点在第三个输入框
TxtInfo.ViewStart = TxtInfo.ViewEnd;
TxtInfo.ViewEnd = TxtInfo.ViewEnd + 60;
InitTxt();//初始化
InvalidateRect(hWndGameView, 0, true);//更新窗口
UpdateWindow(hWndGameView);
}
TxtInfo.PartBuffer[0] = 0;
TmpChar = 0;
MOVE = 0;
RowGrow(NUMBEREDIT);
SetFocus(hWndEdit[NUMBEREDIT - 1]);
}
wchar_t tmplater[2];
int seek = 0;
if (wWord == 8) {
for (; TxtInfo.PartBuffer[seek] != 0; seek++);
if (!seek)
TxtInfo.PartBuffer[0] = 0;
else {
TxtInfo.PartBuffer[seek - 1] = 0;
}
}
else {
tmplater[0] = TmpChar;
tmplater[1] = 0;
wcscat_s(TxtInfo.PartBuffer, tmplater);
for (; TxtInfo.PartBuffer[seek] != 0; seek++);
}
if (NUMBEREDIT == 1) {
SetDlgItemText(hWndGameView, EditInput3, L"");
}
SetDlgItemText(hWndGameView, EditInput1 + (NUMBEREDIT - 1), TxtInfo.PartBuffer);
SendMessage(hWndEdit[NUMBEREDIT - 1], EM_SETSEL, seek, seek);
SendMessage(hWndProgressBar, PBM_SETPOS, (WPARAM)TxtInfo.MoveAddress, 0);
double tmpBar = ((double)TxtInfo.MoveAddress / (double)TxtInfo.TXTSIZE);
int tmpView = (int)(tmpBar * 100);
wchar_t viewint[10];
viewint[0] = 0;
_itow_s(tmpView, viewint, 10);
SetDlgItemText(hWndGameView, PgIndex, viewint);
if (TxtInfo.MoveAddress == TxtInfo.TXTSIZE) {
InitTxt();
MOVE = 0;
NUMBEREDIT = 1;
TxtInfo.PartBuffer[0] = 0;
TxtInfo.MoveAddress = 0;
InvalidateRect(hWndStart, 0, true);
UpdateWindow(hWndStart);
MessageBox(hWndGameView, L"congratulation!", L"Caution", MB_OK);
ShowWindow(hWndStart, SW_SHOW);//GameView对话框已经创建,将它显示出来
EndDialog(hWndGameView, 0);
}
}
else {
MessageBox(hWndGameView, L"Error!", L"Caution", MB_OK);
ShowWindow(hWndStart, SW_SHOW);
EndDialog(hWndGameView, 0);
}
return;
}
LRESULT CALLBACK EditProc1(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
CallWindowProc(g_Edit[0], hWnd, message, wParam, lParam);
switch (message)
{
case WM_CHAR:
DealEditCallBack(wParam);
break;
}
return 0;
}
void RdviewFile() {
wchar_t * TmpCurrentPathB = GetCurrentPath(L"MyResource\");//exe所在绝对路径
wcscat_s(TmpCurrentPathB, MAX_PATH, SELECTEDTXT);
HANDLE MFILE = CreateFile(TmpCurrentPathB, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);//windowsAPT获取文件句柄
int Msize = GetFileSize(MFILE, 0);//获取文件大小
if (Msize > 0) {
char *Mtxt = new char[Msize + 1];//为缓冲区申请内存
DWORD Mbyte;//out读取文件的实际大小
ReadFile(MFILE, Mtxt, Msize, &Mbyte, 0);//读取文件保存到缓冲区
Mtxt[Msize] = 0;//为结尾赋零
int unicodelen = MultiByteToWideChar(CP_ACP, 0, Mtxt, -1, 0, 0);
wchar_t * pUnicode;//定义U码指针
pUnicode = new wchar_t[unicodelen];//申请U码缓冲区
MultiByteToWideChar(CP_ACP, 0, Mtxt, -1, pUnicode, unicodelen);//ANSI转换宽字节
TxtInfo.TXTCONTENT = pUnicode;
TxtInfo.TXTSIZE = unicodelen;//字符数
}
else {
TxtInfo.TXTCONTENT = 0;
TxtInfo.TXTSIZE = 0;
}
CloseHandle(MFILE);
return;
}
void RefreshList(HWND lview) {
wchar_t * TmpCurrentPathC = GetCurrentPath(L"MyResource");//exe所在绝对路径
char * filePath = ConvertLPWSTRToLPSTR(TmpCurrentPathC);
vector files;
getFiles(filePath, "txt", files);
int size = files.size(),pos;
string s;
LVITEM vitem;
vitem.mask = LVIF_TEXT;
SendMessage(lview, LVM_DELETEALLITEMS, 0, 0);
for (int i = 0; i < size; i++){
pos = files[i].find_last_of('\');
s = (string)(files[i].substr(pos + 1));
wchar_t * pUnicode = ConvertLPSTRtoLPWSTR(s.c_str());
vitem.pszText = pUnicode;
vitem.iItem = i;
vitem.iSubItem = 0;
SendMessage(lview, LVM_INSERTITEM, i, (long)&vitem);
delete[] pUnicode;
}
delete[] filePath;
}
VOID onPaint(HDC hdc)
{
Graphics graphics(hdc);
FontFamily fontFamily(L"Arial");
Font font(&fontFamily, SizeFont, FontStyleRegular, UnitPixel);
PointF pointFA((PointX), PointY);
SolidBrush solidBrush(GreyFont);
SolidBrush solidBrush1(Color(255, 255, 255, 255));
Pen solidBrush2(Color(255, 0, 0, 0));
int nowaddress = TxtInfo.ViewStart;
int ColumnNum = 20,ColorController=0;
graphics.FillRectangle(&solidBrush1, 46, 95, 633, 40);
graphics.DrawRectangle(&solidBrush2, 46, 95, 633, 82);
graphics.FillRectangle(&solidBrush1, 46, 205, 633, 40);
graphics.DrawRectangle(&solidBrush2, 46, 205, 633, 82);
graphics.FillRectangle(&solidBrush1, 46, 315, 633, 40);
graphics.DrawRectangle(&solidBrush2, 46, 315, 633, 82);
if (EnglishOrChinese) {
ColumnNum = 20;
}
else {
ColumnNum = 20;
}
for (int p = 1; p < 4; p++) {
pointFA.X = PointX;
MeasureStringIntervel=0;
for (int i = 0; i 

