《Windows API每日一练》5.5 插入符号

当你向程序中输入文本时,通常会有下划线、竖线或方框指示你输入的下一个字符将出现在屏幕上的位置。你也许认为这是“光标”,但在编写Windows程序时,你必须避免这种习惯。在Windows中,它被称为“插入符号”(caret)。“光标”(cursor)特指表示鼠标位置的位图图像,即鼠标指针。

本节必须掌握的知识点:

        关于插入符号的函数

        第34练:文本编辑器插入符号

5.5.1 关于插入符号的函数

■Windows中有五个基本的插入符号函数:

●CreateCaret:创建和窗口关联的插入符号。

●SetCaretPos:设置窗口内的插入符号的位置。

●ShowCaret:显示插入符号。

●HideCaret:险藏插入符号。

●DestroyCaret:销毁插入符号。

操作插入符的函数

此外,还有用于获得当前插入符号位置的函数(GetCaretPos)与获得和设置插入符号闪烁时间的函数(GetCaretBlinkTime 和 SetCaretBlinkTime)。

●GetCaretPos 函数的原型:

BOOL GetCaretPos(

  LPPOINT lpPoint // 接收光标位置的指针

);

返回值:

如果函数调用成功,返回值为非零值(TRUE)。

如果函数调用失败,返回值为零(FALSE)。

使用 GetCaretPos 函数可以获取当前光标的位置,即光标在屏幕上的坐标。通过传递一个指向 POINT 结构的指针,函数将光标的坐标信息存储在该结构中。

示例代码:

#include <Windows.h>

int main() {

    POINT caretPos;

    // 获取光标位置

    if (GetCaretPos(&caretPos)) {

        // 输出光标的坐标

        printf("Caret position: x = %d, y = %d\n", caretPos.x, caretPos.y);

    } else {

        // 获取光标位置失败

        printf("Failed to get caret position.\n");

    }

    return 0;

}

●GetCaretBlinkTime函数原型:

UINT GetCaretBlinkTime();

返回值:

返回一个无符号整数,表示光标闪烁的时间间隔(以毫秒为单位)。

使用 GetCaretBlinkTime 函数可以获取当前系统中光标闪烁的时间间隔。光标闪烁是指光标在显示和隐藏之间的交替效果。

示例代码:

#include <Windows.h>

int main() {

    UINT blinkTime = GetCaretBlinkTime();

    // 输出光标闪烁时间间隔

    printf("Caret blink time: %u ms\n", blinkTime);

    return 0;

}

●SetCaretBlinkTime函数原型:

BOOL SetCaretBlinkTime(

  UINT uMSeconds // 光标闪烁的时间间隔(以毫秒为单位)

);

返回值:

如果函数调用成功,返回值为非零值(TRUE)。

如果函数调用失败,返回值为零(FALSE)。

使用 SetCaretBlinkTime 函数可以设置光标闪烁的时间间隔。你可以将所需的时间间隔(以毫秒为单位)作为参数传递给该函数。

示例代码:

#include <Windows.h>

int main() {

    UINT blinkTime = 500; // 设置光标闪烁时间间隔为 500 毫秒

    // 设置光标闪烁时间间隔

    if (SetCaretBlinkTime(blinkTime)) {

        printf("Caret blink time set successfully.\n");

    } else {

        printf("Failed to set caret blink time.\n");

    }

    return 0;

}

处理插入符

在Windows中,插入符号通常是一个字符大小的水平线或方框,或是与字符高度相一 致的竖线。使用变宽字体的时候推荐使用竖线插入符号,比如Windows默认系统字体。因 为变宽字体字符不是固定大小的,不能把水平线或者方框设置为字符的大小。

如果你的程序中需要插入符号,那么不应该简单地在窗口过程的WM_CREATE消息中创建它并在WM_DESTROY消息中销毁它。不建议你这样做的原因是一个消息队列仅能够支持一个插入符号。因此,如果你的程序有多于一个窗口,则多个窗口必须有效地共享同一个插入符号。

这并不像听起来那样有限制性。你想一下,仅当窗口具有输入焦点时,窗口中插入符号的显示才有意义。的确,闪烁的插入符号的存在是一种视觉提示:它让用户意识到他可以向程序中输入文本。因为任何时候仅有一个窗口具有输入焦点,所有多个窗口同时使插入符号闪烁是没有意义的。

程序能通过处理WM_SETFOCUS消息和WM_KILLFOCUS消息来决定它是否具有输入焦点。正如名称所暗示的,当窗口过程接收输入焦点时,它接收到一个WM_SETFOCUS 消息:当它失去输入焦点时,收到一个WM_KILLFOCUS消息。这些消息成对出现:窗口过程在接收到一条WM_KILLFOCUS消息前,总是会接收到一条WM_SETFOCUS消息。 并且在窗口的生命期窗口过程总是接收到相同数目的WM_SETFOCUS消息和 WM_KILLFOCUS 消息。

使用插入符号的主要规则很简单:在窗口过程处理WM_SETFOCUS消息时调用 CreateCaret 函数,处理 WM_KILLFOCUS 消息时调用 DestroyCaret 函数。

还有一些其他规则:创建的插入符号是隐藏的。在调用CreateCaret之后,窗口过程必 须调用ShowCaret使之可见。另外,如果窗口过程处理的是一个非WM_PAINT消息,但要在窗口内绘制某些东西时,它必须调用HideCaret隐藏插入符号。当它结束在窗口内的绘制之后,再调用ShowCaret来显示插入符号。HideCaret的效果是叠加的:如果你调用了HideCaret很多次,但没调用过ShowCaret,那么你必须再调用同样次数的ShowCaret才能使插入符号可见。

5.5.2 第34练:文本编辑器插入符号

/*------------------------------------------------------------------

034  WIN32 API 每日一练

     第34个例子TYPER.C:文本编辑器---插入符号

     GetFocus函数

     CreateCaret函数     

     SetCaretPos函数

     ShowCaret函数

     HideCaret函数

     DestroyCaret函数

     GetCaretPos函数

     WM_SETFOCUS消息

     WM_KILLFOCUS消息

注:TYPER程序 不允许使用 双字节宽度的字符

无搜索,替换,保存文件,拼写检查,滚动,帮助等扩展功能。

(c) www.bcdaren.com, 2020

----------------------------------------------------------------*/

#include <windows.h>

#define BUFFER(x,y) *(pBuffer + y * cxBuffer + x)

TCHAR * pBuffer = NULL;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

 PSTR szCmdLine, int iCmdShow)

{

     static TCHAR szAppName[] = TEXT("Typer");

    (略)

     return msg.wParam;

}

LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)

{

     static DWORD dwCharSet = DEFAULT_CHARSET ;//#define DEFAULT_CHARSET 1

     static int cxChar, cyChar, cxClient, cyClient,

cxBuffer, cyBuffer,xCaret, yCaret ;

     //static TCHAR * pBuffer = NULL ;

     HDC hdc ;

     int x, y, i ;

     PAINTSTRUCT ps ;

     TEXTMETRIC tm ;

     switch (message)

     {

     case WM_INPUTLANGCHANGE:

          dwCharSet = wParam

          //继续执行下去

     case WM_CREATE:

          hdc = GetDC (hwnd) ;

          //创建逻辑字体

          SelectObject ( hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,

          dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;

          //获取字体信息

          GetTextMetrics (hdc, &tm) ;

          cxChar = tm.tmAveCharWidth ;

          cyChar = tm.tmHeight ;

          DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;

          ReleaseDC (hwnd, hdc) ;

          //继续执行下去

     case WM_SIZE:

          // 获取窗口大小,以像素为单位

          if (message == WM_SIZE)

          {

               cxClient = LOWORD (lParam) ;

               cyClient = HIWORD (lParam) ;

          }

          // 计算窗口大小(字符)

          cxBuffer = max (1, cxClient / cxChar) ; //cxBuffer窗口字符宽度

          cyBuffer = max (1, cyClient / cyChar) ; //cyBuffer窗口字符高度

          // 为缓冲区分配内存并清除它 

          if (pBuffer != NULL)

               free (pBuffer) ;

          //创建保存窗口所有字符的缓冲区

          pBuffer = (TCHAR *) malloc (cxBuffer * cyBuffer * sizeof (TCHAR)) ;

          //初始化pBuffer为一个‘ ’空字符

          for (y = 0 ; y < cyBuffer ; y++)

               for (x = 0 ; x < cxBuffer ; x++)

                    BUFFER(x,y) = ' ' ;

          // 插入符号定位左上角

          xCaret = 0 ;

          yCaret = 0 ;

          if (hwnd == GetFocus ()) //插入符号为当前窗口---焦点窗口

//设置插入符号位置

               SetCaretPos (xCaret * cxChar, yCaret * cyChar) ;

          InvalidateRect (hwnd, NULL, TRUE) ; //重绘窗口

          return 0 ;

     //获得键盘焦点后发送到窗口

     case WM_SETFOCUS:

          // 创建和显示插入符号,指定大小,NULL表示实心

          CreateCaret (hwnd, NULL, cxChar, cyChar) ;

          SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; //设置符号位置

          ShowCaret (hwnd) ; //显示符号

          return 0 ;

     //在失去键盘焦点之前立即发送到窗口

     case WM_KILLFOCUS:

          // 隐藏和删除插入符号

          HideCaret (hwnd) ; //隐藏符号

          DestroyCaret () ; //删除符号

          return 0 ;

     //击键消息---光标移动的处理

     case WM_KEYDOWN:

          switch (wParam)

          {

          case VK_HOME:

               xCaret = 0 ;

               break

          case VK_END:

               xCaret = cxBuffer - 1 ;

               break

          case VK_PRIOR: //Page Up

               yCaret = 0 ;

               break

          case VK_NEXT: //PageDown

               yCaret = cyBuffer - 1 ;

               break

          case VK_LEFT:

               xCaret = max (xCaret - 1, 0) ;

               break

          case VK_RIGHT:

               xCaret = min (xCaret + 1, cxBuffer - 1) ;

               break

          case VK_UP:

               yCaret = max (yCaret - 1, 0) ;

               break

          case VK_DOWN:

               yCaret = min (yCaret + 1, cyBuffer - 1) ;

               break

          case VK_DELETE: //注意VK_BACK键当字符消息去处理,这里只处理VK_DELETE

               //将该行从当前位置后面字符依次前移一格

               for (x = xCaret ; x < cxBuffer - 1 ; x++)

                    BUFFER(x, yCaret) = BUFFER(x + 1, yCaret);

               BUFFER(cxBuffer - 1, yCaret) = ' ';//该行最后一个设为空字符。

               //绘图前必须先隐藏插入符

               HideCaret(hwnd);

               hdc = GetDC(hwnd);

               SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,

                    dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));

               TextOut(hdc, xCaret * cxChar, yCaret * cyChar,

                    &BUFFER(xCaret, yCaret),

                    cxBuffer - xCaret); //重新输出该行xCaret后面的文字

               DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));

               ReleaseDC(hwnd, hdc);

               ShowCaret(hwnd);

               break;

          }

               SetCaretPos(xCaret * cxChar, yCaret * cyChar);

               return 0;

         

     //字符消息

     case WM_CHAR:

          for (i = 0 ; i < (int) LOWORD (lParam) ; i++)

          {

               //转义字符处理

               switch (wParam)

               {

               case '\b': // 退格

                    if (xCaret > 0)

                    {

                         xCaret--;

//1为repeat字段的值

                         SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);

                    }

                    break;

               case '\t': //Tab键,8个字符长度

                    do

                    {

                         SendMessage (hwnd, WM_CHAR, ' ', 1) ;

                    } while (xCaret % 8 != 0) ;

                    break ;

               case '\n': //换行符,改变y到下一行,x坐标没变。

                    if (++yCaret == cyBuffer)

                         yCaret = 0 ;

                    break ;

               case '\r': // 回车

                    xCaret = 0;

                    if (++yCaret == cyBuffer)

                         yCaret = 0;

                    break;

                //ESC键,清空屏幕 \xhh表示1到2位十六进制所代表的任意字符

               case '\x1B':

                    for (y = 0; y < cyBuffer; y++)

                         for (x = 0; x < cxBuffer; x++)

                              BUFFER(x, y) = ' ';

                    xCaret = 0;

                    yCaret = 0;

                    InvalidateRect(hwnd, NULL, FALSE);

                    break;

               default: // 字符编码

                    BUFFER(xCaret, yCaret) = (TCHAR)wParam;

                    HideCaret(hwnd);

                    hdc = GetDC(hwnd);

                    SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,

                         dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));

                    TextOut(hdc, xCaret * cxChar, yCaret * cyChar,

                         &BUFFER(xCaret, yCaret), 1);

                    DeleteObject(

                         SelectObject(hdc, GetStockObject(SYSTEM_FONT)));

                    ReleaseDC(hwnd, hdc);

                    ShowCaret(hwnd);

                    if (++xCaret == cxBuffer)

                    {

                         xCaret = 0;

                         if (++yCaret == cyBuffer)

                              yCaret = 0;

                    }

                    break;

               }

          } 

          SetCaretPos (xCaret * cxChar, yCaret * cyChar) ;

          return 0 ;

     case WM_PAINT:

          hdc = BeginPaint (hwnd, &ps) ;

          SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,

               dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;

          for (y = 0 ; y < cyBuffer ; y++)

               TextOut (hdc, 0, y * cyChar, & BUFFER(0,y), cxBuffer) ;

          DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;

          EndPaint (hwnd, &ps) ;

          return 0 ;

     case WM_DESTROY:

          if (pBuffer != NULL) free(pBuffer);

          PostQuitMessage(0);

          return 0;

     }

     return DefWindowProc (hwnd, message, wParam, lParam) ;

}

/*************************************************************************

CreateCaret:创建和窗口关联的插入符号

SetCaretPos:设置窗口内的插入符号的位置

ShowCaret   :显示插入符号

HideCaret   :隐藏插入符号

DestroyCaret:销毁插入符号

GetCaretPos :当前插入符号的位置

GetFocus函数:检索具有键盘焦点的窗口的句柄

**************************************************************************

WM_SETFOCUS消息:获得键盘焦点后发送到窗口

#define WM_SETFOCUS     0x0007

参数:wParam

失去键盘焦点的窗口句柄。此参数可以为NULL。

lParam:不使用此参数。

**************************************************************************

WM_KILLFOCUS消息:在失去键盘焦点之前立即发送到窗口

#define WM_SETFOCUS     0x0008

参数:wParam

失去键盘焦点的窗口句柄。此参数可以为NULL。

lParam:不使用此参数。

*/

       运行结果:

图5-7 文本编辑器插入符

 

总结

       实例TYPER.C创建了一个简单的文本编辑器。

●窗口过程首先通过M_INPUTLANGCHANGE消息获取当前字符集,然后在WM_CREATE消息中选入新创建的逻辑字体,并获取新逻辑字体的字符宽和高。

       ●在WM_SIZE消息中,当窗口客户区大小发生变化时,使用空格字符清空窗口客户区,然后将插入符置于窗口客户区的左上角,然后重绘窗口。【注意】前提时当前窗口为焦点窗口。

       ●当窗口获得焦点时,窗口过程处理M_SETFOCUS消息,创建、设置并显示插入符。

       ●当窗口失去焦点时,窗口过程处理WM_KILLFOCUS消息,隐藏并删除插入符。

       ●在按键消息WM_KEYDOWN中,处理HOME、END、PageUp、PageDown和上下左右箭头键,计算插入符的新位置。处理Delete键稍微复杂一些,先将当前位置后面的字符依次前移一个位置,并在该行字符的结尾处添加一个空格。【注意】移动字符时,先隐藏插入符,再选入新创建的逻辑字体绘制字符,绘制完成后重新显示插入符。所有字符重新绘制完成后,重新将插入符置于该行字符的结尾处。

       ●窗口过程在WM_CHAR消息代码块绘制字符。我们把字符分为两类:

       转义字符处理:

'\b'将字符位置减一,然后转为Delete按键消息处理。

'\t':转换为空格字符消息处理。

'\n':将字符yCaret加一(行数加一)。

'\r':将字符xCaret清零(列数清零)。

'\x1B':ESC键清空屏幕(窗口客户区输出空格),并将插入符坐标清零。

       正常可见字符的处理:与Delete键的处理过程类似,先隐藏插入符,然后绘制字符后再重新显示插入符,再将插入符置于下一个位置。

       ●WM_PAINT消息选入新的逻辑字体绘制所有字符。

       【注意】

       实例创建的简单文本编辑器并不完善,并不支持复制、粘贴、剪切、选择等功能,也没有对应的菜单选项。我们将在后续的章节中逐步完善此文本编辑器。

此外,当我们输入中文字符时,需要收到将插入符后移一个位置,等待输入下一个字符。

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值