当前位置:C++技术网 > 精选软件 > 支持移动光标后的退格键前向删除文字和Delete键后向删除文字功能实现

支持移动光标后的退格键前向删除文字和Delete键后向删除文字功能实现

更新时间:2016-01-06 00:07:05浏览次数:1+次

    当支持光标移动后,再支持删除文字,可就不如之前只是在末尾往前删除文字那么简单。如果你还没有准备好,你可以先阅读从后往前删除文字的文章分析《win32使用退格键BackSpace从后到前的删除字符串的字符》。从后往前删除,则是需要替换最后一个字符即可。
    然而加入了移动光标,那么删除就很自由了。所以我们要重新设计删除的逻辑。
    当然最开始,我们要先支持退格键和delete按键的响应,因为是控制删除字符,所以就在WM_KEYDOWN消息中处理比较好。如果你不知道这些按键的ASCII编码,请点击这里查询ASCII编码表
    我们查得,退格键的编码为8,delete键的编码为127,都是十进制的数。所以我们在WM_CHAR中判断wParam参数即可。
    我们要确定的是光标工作的方式。我们支持光标自由移动,也要支持自由删除,所有对于光标的工作方式要很清楚,才能完成这个功能。

    下面看一张文本编辑中光标指示位置的工作原理图,如下所示:

文本编辑中光标指示位置的工作原理图

【文本编辑中光标指示位置的工作原理图】
    当光标位置为0时,从上面的的文档中可以看到下面标了一个很小的0字,表示当前光标的位置为0。在内存中,光标指向索引为0的元素。那么此时使用退格键删除,是没有任何作用的,因为前面没有文字了。当光标移动到位置2时,就在第一个+后面,内存中就指向了第二个+号。那么此时退格键删除的就是第一个加号。那么也就是说,退格键是删除当前光标指向的数组元素的前一个元素。
    那么与退格键删除字符的原理差不多,Delete键则删除当前指向的这个字符。我想此时你就不难理解为什么delete删除的是当前指向的字符了吧。这个逻辑是源于对于普通大众的体验。我们做的功能也要符合这个体验,才不会让人感觉很奇怪。我们的程序只是没有加入闪动的光标了。
    而删除,实际上就是移动字符串。如果退格键往前删除,就是将当前指向的往后的所有字符往前移动。而如果是Delete往后删除,那么就是当前光标指向的后一个字符开始的后面所有字符往前移动。所以,两种删除方式,只是移动的起点不同罢了。这个插入字符的移动文字方向刚好相反。插入字符的说明文章,请阅读《win32实现自由移动光标以插入模式编辑文字以及防止破坏字符数组》。
    这里要注意一点,在移动光标时,为了能够实时显示出当前光标的字符,实际上显示的是光标的前一个字符。我们只给一种错觉而已。然而在我们要删除字符时,这个值会很重要。所以就要精确的显示。我们每次添加字符后,光标自动往后移动了一个,所以每次都取不到字符,就显示不了字符。为了删除功能正常的让人理解,就矫正一下这个显示。当你移动光标时就会发现能够显示当前光标所在的字符了。
    如果最后能够加上一个光标来指示光标位置,而不是用文字提示,就完美了。这个以后再说。
    完整的代码如下:
#include "windows.h"
#include <tchar.h>
#define MAX 20
static TCHAR cjjjs1[]=_T("提示1:Home回到开头,End回到结尾,PageUp向前移动10个字,PageDown向后移动10个字");
static TCHAR cjjjs2[]=_T("提示2:键盘光标↑和←可以向左移动一个位置,光标→和↓可以向右移动一个位置");
TCHAR txt[MAX]=_T("");
TCHAR tip[100]=_T("");
bool bIsInsert=true;//默认插入模式
// - 项目是Unicode字符集
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static int iPosInput=0;
    switch (message)
    {
     case WM_PAINT:
         {
             hdc = BeginPaint(hwnd,&ps);
             //设置等宽字体
             SelectObject(hdc,GetStockObject(SYSTEM_FIXED_FONT));

             //当前光标位置的字符
             wsprintf(tip,_T("当前光标所在的字符:%c"),txt[iPosInput?iPosInput:0]);
             TextOut(hdc,0,170,tip,lstrlen(tip));

             //提示
             TextOut(hdc,0,130,cjjjs1,lstrlen(cjjjs1));
             TextOut(hdc,0,150,cjjjs2,lstrlen(cjjjs2));

             TCHAR insTip[100]=_T("");
             wsprintf(insTip,_T("当前编辑状态:%s"),bIsInsert?_T("插入模式"):_T("覆盖模式"));
             TextOut(hdc,0,190,insTip,lstrlen(insTip));

             //输出文字内容
             TextOut(hdc,0,0,txt,lstrlen(txt));
             EndPaint(hwnd,&ps);
         }
         return 0;
     case WM_KEYDOWN:
         {
             //默认直接覆盖
             switch(wParam)
             {
             case VK_UP://左移一个字
             case VK_LEFT:
                 iPosInput+=-1;
                 break;
             case VK_RIGHT://右移一个字
             case VK_DOWN:
                 iPosInput+=1;
                 break;
             case VK_PRIOR://Page Up键,左移10个字
                 iPosInput+=-10;
                 break;
             case VK_NEXT://Page Down键,右移10个字
                 iPosInput+=10;
                 break;
             case VK_HOME://Home键,移动开头
                 iPosInput=0;
                 break;
             case VK_END://End键,移到结尾最后一个字符位置
                 iPosInput=lstrlen(txt);
                 break;
             case VK_INSERT://End键,移到结尾最后一个字符位置
                 bIsInsert=!bIsInsert;
                 break;
             case VK_DELETE:
                 {
                     //将光标所在的位置删除
                     int len = lstrlen(txt);
                     if (iPosInput==len)return 0;//向后删除,光标在结尾处,也没有字符可删除

                     for (int i=0;i<=len-iPosInput;i++)
                     {
                         txt[iPosInput+i]=txt[iPosInput+1+i];
                     }
                     InvalidateRect(hwnd,NULL,TRUE);
                 }
                 break;
             case VK_BACK:
                 {
                     //将光标前一个字符删除,后面的字符向前移动
                     if (iPosInput==0)return 0;//向前删除,光标在0处没有字符可删除

                     int len = lstrlen(txt);

                     for (int i=0;i<=len-iPosInput;i++)
                     {
                         txt[iPosInput-1+i]=txt[iPosInput+i];
                     }
                     iPosInput--;
                     InvalidateRect(hwnd,NULL,TRUE);
                 }
             default:
                 break;
             }
             //规整文字输入的位置,以免溢出
             if (iPosInput<0)
                 iPosInput=0;
             else if (iPosInput>lstrlen(txt))
                 iPosInput=lstrlen(txt);
             else if(iPosInput>MAX)
                 iPosInput=MAX;
         }
         InvalidateRect(hwnd,NULL,TRUE);
         return 0;
        case WM_CHAR:
            {
                TCHAR ch=wParam;
                if (ch==8)return 0;//屏蔽掉退格符的输入,在WM_KEYDOWN执行删除
                if (bIsInsert)
                {
                    //插入字符
                    //将插入点后面的字符全部后移
                int len = lstrlen(txt);
                if (len>=MAX-2)
                {
                    //超出长度的字符,截断处理。必须减2才能防止溢出。
                    len=MAX-2;
                }
                for (int i=0;i<len-iPosInput;i++)
                {
                    txt[len-i]=txt[len-i-1];
                }
                                }
                if (iPosInput==MAX-1)
                {
                    iPosInput=MAX-2;//防止溢出
                }
                txt[iPosInput]=ch;
                iPosInput++;
            }
            InvalidateRect(hwnd,NULL,TRUE);
            return 0;
     case WM_DESTROY:
         PostQuitMessage(0);
         return 0;
     default:
         break;//跳出到默认处理
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,LPSTR lpCmd,int iShow)
{
    TCHAR ClassName[] = _T("MyClass");
    TCHAR title1[] = _T("C++技术网http://www.cjjjs.com");
    WNDCLASS wndClass;
    wndClass.cbClsExtra=0;
    wndClass.cbWndExtra=0;
    wndClass.hbrBackground= (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.hCursor=LoadCursor(NULL,IDC_HAND);
    wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    wndClass.hInstance = hInstance;
    wndClass.lpfnWndProc = WinProc;
    wndClass.lpszClassName = ClassName;
    wndClass.lpszMenuName=NULL;
    wndClass.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClass(&wndClass))return 0;
    HWND hwnd = CreateWindow(ClassName,title1,WS_OVERLAPPEDWINDOW,0,0,650,400,NULL,NULL,hInstance,NULL);
    ShowWindow(hwnd,SW_SHOWNORMAL);

    MSG msg;
    while (GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}