专栏首页程序员互动联盟【Windows编程】系列第十篇:文本插入符

【Windows编程】系列第十篇:文本插入符

大家知道,在使用微软的编程环境创建工程时会让你选择是控制台模式还是Windows应用程序。如果选择控制台的console模式,就会在运行时出现一个黑洞洞的字符模式窗口,里面就有等待输入一闪一闪的插入符。输入光标从DOS时代就存在,但是在Win32中赋予了更强大的功能。下图就是Windows的CMD窗口,其中的输入点就是插入光标:

要注意的是这里的插入符或插入光标并不是Windows中另外一个“光标”,这里是指示插入字符的位置,而不是用于鼠标,手写输入等可以定位、移动的光标(Cursor),而是插入符Caret,本文也成为插入光标,注意插入二字,为了方便,以下在本文中也简称为光标或插入符,但要注意此光标非彼光标。

为什么会有插入光标(插入符)?了解了这个基本问题,就成功了一半了。我们知道计算机可以通过键盘来输入各种字符和控制符,那么自然就存在一个问题,输入的字符应该放到屏幕的什么位置?这个就是光标产生的原因,光标实际上就是一个字符插入标识。简单的说就是我们想把字符输入到哪个位置,就先把插入符设置到那里,设置时其实就是告诉电脑输入的字符你给我在哪里显示!从这个我们也可以推断,插入符在同一时刻只能有一个。

  • 光标相关API函数

要使用光标,首先得创建一个光标,创建光标的API函数为:

BOOL CreateCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight);

hWnd参数表示光标是属于哪个窗口。

hBitmap参数是一个位图的句柄,计算机将使用这个句柄的位图来作为光标的形状。

既然光标是给使用电脑的人插入字符用的,那就得有形状让使用者能看到,因此光标需要有一个可见的小图标。当然为了不同的情况和需求,Windows让我们可以自定义光标的形状。常见的位图创建或者加载API函数如CreateBitmap、CreateDIBitmap、LoadBitmap等都可以创建或加载一个位图,将句柄作为该参数。

nWidth和nHeight分别是位图的宽和高。

光标创建之后是不会自动显示的,也就是默认是隐藏状态,需要主动调用下面的显示函数:

BOOL ShowCaret(HWND hWnd);

当然有显示光标也可以隐藏光标:

BOOL HideCaret(HWND hWnd);

以上两个函数的参数很简单,都是要显示窗口的句柄。

要设置插入符的位置,使用下面API函数:

BOOL SetCaretPos(int X, int Y);

参数X、Y分别是相对于窗口客户区的坐标。

我们可以用如下API函数获取当前光标的位置:

BOOL GetCaretPos(LPPOINT lpPoint);

参数lpPoint返回当前光标所在的位置。

我们知道光标会闪烁,这个闪烁的时间间隔是可以设置的,我们可以用如下API来设置和获取插入光标的闪烁时间:

BOOL SetCaretBlinkTime(UINT uMSeconds);
UINT GetCaretBlinkTime(VOID);

参数uMSeconds为闪烁的间隔毫秒数。

最后不再使用时需要销毁光标:

BOOL DestroyCaret(VOID);
  • 光标处理相关消息

与插入光标相关的消息主要有WM_SETFOCUS、WM_KILLFOCUS。通常在WM_SETFOCUS中创建和显示光标,而在WM_KILLFOCUS中销毁光标。一般应有中再结合WM_KEYDOWN和WM_CHAR消息,实现文本的输入。

  • 光标应用实例

以下是一个简单的虚拟终端,我们常见的很多终端软件都是这样来实现的,比如常见的SecureCRT、Tera Term、XShell、putty等等。本实例就是用了插入光标来实现字符输入、插入,部分关键代码如下,完整实例代码请猛戳左下角阅读原文

#include <windows.h>

//处理字符输入
static void DrawChar(HDC hDC, int x, int y, TCHAR *str, int num)
{
    RECT rect;
    SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));
    SetTextColor(hDC, TextColor);
    SetBkMode(hDC, TRANSPARENT);
    rect.left = x;
    rect.top = y;
    rect.right = x + num * nCharWidth;
    rect.bottom = y + nCharHeight;
    FillRect(hDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
    TextOut(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight, &TEXTMATRIX(nCaretPosX, nCaretPosY), nLineChars - nCaretPosX);
}

static LRESULT CALLBACK VTWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int x, y;
HDC hDC;

switch (message)
{
case WM_CREATE:
   hDC = GetDC(hWnd);
   SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));
   GetTextMetrics(hDC, &tm);
   ReleaseDC(hWnd, hDC);
   nCharWidth = tm.tmAveCharWidth;
   nCharHeight = tm.tmHeight;
   TextColor = RGB(255, 255, 255);
   nCaretOffsetY = 12;
   return 0;

case WM_SIZE:
   nVTWidth = LOWORD(lParam);
   nLineChars = max(1, nVTWidth/nCharWidth);
   nVTHeight = HIWORD(lParam);
   nRowChars = max(1, nVTHeight/nCharHeight);
        
   if (pTextMatrix != NULL)
   {
     free(pTextMatrix);
   }
   pTextMatrix = (TCHAR *)malloc(nLineChars * nRowChars);
   if (pTextMatrix)
   {
     for (y=0; y<nRowChars; y++)
     {
       for (x=0; x<nLineChars; x++)
       {
         TEXTMATRIX(x, y) = TEXT(' ');
       }
     }
   }
   SetCaretPos(0, nCaretOffsetY);
   return 0;

case WM_LBUTTONDOWN:
   SetFocus(hWnd);
   break;

case WM_KEYDOWN:
   switch (wParam)
   {
   case VK_HOME:  
     nCaretPosX = 0;
     break; 

   case VK_END: 
     nCaretPosX = nLineChars - 1;
     break; 

   case VK_PRIOR: 
     nCaretPosY = 0; 
     break; 

   case VK_NEXT:
     nCaretPosY = nRowChars -1; 
     break; 

   case VK_LEFT: 
     nCaretPosX = max(nCaretPosX - 1, 0); 
     break; 

  case VK_RIGHT:  
     nCaretPosX = min(nCaretPosX + 1, nLineChars - 1); 
     break; 

  case VK_UP:  
     nCaretPosY = max(nCaretPosY - 1, 0); 
     break; 

  case VK_DOWN: 
     nCaretPosY = min(nCaretPosY + 1, nRowChars - 1); 
     break; 

  case VK_DELETE:  
     for (x = nCaretPosX; x < nLineChars; x++) 
       TEXTMATRIX(x, nCaretPosY) = TEXTMATRIX(x + 1, nCaretPosY); 
     TEXTMATRIX(nLineChars - 1, nCaretPosY) = ' '; 
     HideCaret(hWnd); 
     hDC = GetDC(hWnd); 
     DrawChar(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight,&TEXTMATRIX(nCaretPosX, nCaretPosY), nLineChars - nCaretPosX/nCharWidth);
     ReleaseDC(hWnd, hDC); 
     ShowCaret(hWnd); 
   break; 
 } 
 SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY); 
    return 0;

case WM_SHOWWINDOW:
  SetFocus(hWnd);
  break;

case WM_SETFOCUS:
  CreateCaret(hWnd, NULL, nCharWidth, 2);
  SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY);
  ShowCaret(hWnd);
  break;

case WM_KILLFOCUS:
case WM_DESTROY:
  HideCaret(hWnd);
  DestroyCaret();
  break;

case WM_CHAR:
  switch (wParam)
  {
    case 0x08: 
     if (nCaretPosX > 0)
     {
        nCaretPosX--;
        SendMessage(hWnd, WM_KEYDOWN, VK_DELETE, 1L);
     }
     break;
    case 0x09: 
     do
     {
       SendMessage(hWnd, WM_CHAR, TEXT(' '), 2L);
     } while (nCaretPosX % 4 != 0);
     break;
    case 0x0D: 
      nCaretPosX = 0;
       if (++nCaretPosY == nRowChars)
       {
          nCaretPosY = 0;
       }
       break;
     case 0x1B:
     case 0x0A: 
       MessageBeep((UINT)-1);
       break;
     default:
       TEXTMATRIX(nCaretPosX, nCaretPosY) = (TCHAR)wParam;
       HideCaret(hWnd);
       hDC = GetDC(hWnd);
       DrawChar(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight, &TEXTMATRIX(nCaretPosX, nCaretPosY), 1);
       ReleaseDC(hWnd, hDC);
       ShowCaret(hWnd);
       if (++nCaretPosX == nLineChars)
       {
           nCaretPosX = 0;
           if (++nCaretPosY == nRowChars)
           {
              nCaretPosY = 0;
           }
       }
       break;
    }
    SetCaretPos(nCaretPosX * nCharWidth, nCaretPosY * nCharHeight + nCaretOffsetY);
    return 0;

 case WM_PAINT:
 {
    PAINTSTRUCT ps;
    hDC = BeginPaint(hWnd, &ps);
    SelectObject(hDC, GetStockObject(SYSTEM_FIXED_FONT));
    SetBkMode(hDC, TRANSPARENT);
    SetTextColor(hDC, TextColor);
    for (y=0; y<nLineChars; y++)
    {
      TextOut(hDC, 0, y * nCharHeight, &TEXTMATRIX(0, y), nLineChars);
    }
    EndPaint(hWnd, &ps);
 }
 return 0;
 default:
  break;
 }

 return DefWindowProc (hWnd, message, wParam, lParam);}

主程序调用上面的CreateVirtualTerminal函数来创建一个窗口。

示例程序运行后,在窗口中输入部分文本(模仿上面的cmd窗口^_^),效果如下:

本例实现了一个简单的终端模拟小程序,为了读者重用方便,我将终端模拟的小窗口单独作为一个完整的源文件,并且把窗口背景设为黑色,前景色设为白色,看起来更像CMD、Linux等命令行窗口。

更多经验交流可以加入Windows编程讨论QQ群454398517

转载请注明出处http://www.coderonline.net/?p=1905,谢谢合作!

本文分享自微信公众号 - 程序员互动联盟(coder_online)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2015-08-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 吉娃娃还是松饼?找到最好的计算机视觉API

    下面这张在网上流传的图片展示了吉娃娃和松饼之间惊人的相似之处。这些图像通常在人工智能(AI)行业(包括我自己)的演示中共享。 ? 但有一个问题没有人回答过:在...

    AiTechYun
  • Extjs4.2+webAPI+EF实现分页以及webapi的数据传值(续)

    现在领导又要增加功能,需要分页的时候,每页显示N条信息。由于是每个页面都要改,所有需要声明了一个扩展类代码如下: // Copyright : 欧蓝德畅电子技术...

    hbbliyong
  • python codis集群客户端(一) - 基于客户端daemon探活与服务列表维护

    在使用codis时候,我们遇到的场景是,公司提供了HA的Proxy(例如N个),但是不暴露zookeeper(也就是说没有codis后端服务列表)。 如果暴露z...

    用户1225216
  • 【实践操作】:六步教你如何用开源框架Tensorflow对象检测API构建一个玩具检测器

    TensorFlow对象检测API是一个建立在TensorFlow之上的开源框架,可以轻松构建,训练和部署对象检测模型。 到目前为止,API的性能给我留下了深刻...

    AiTechYun
  • WebAPI返回数据类型解惑 以及怎样解决Extjs无法解析返回的xml

     最近开始使用WebAPI,上手很容易,然后有些疑惑   1.WebAPI默认返回什么数据类型,json还是xml?   2.怎么修改WebAPI的返回数据类型...

    hbbliyong
  • python codis集群客户端(二) - 基于zookeeper对实例创建与摘除

     在这一篇中我们实现了不通过zk来编写codis集群proxys的api, 如果codis集群暴露zk给你的话,那么就方便了,探活和故障摘除与恢复codis集群...

    用户1225216
  • 开发者可以调用微信小店货架上的商品了

      通过前面微信小店怎么开那篇文章我们学了开通微信小店,产品也上传好了,如何推广这些商品呢?现在货架开放了,开发者可以调用微信小店货架上的商品,即在自己的页面上...

    ytkah
  • 使用新的谷歌TensorBoard API,让你的机器学习可视化

    谷歌在2015年开源TensorFlow时,包含了一套用于检查理解并运行你的TensorFlow模型的可视化工具TensorBoard。Tensorboard包...

    AiTechYun
  • Redis实现分布式锁

    之前写过一篇博客,里面吭哧吭哧半天,使用Redis实现了一个分布式锁。 今天闲来没事看源码,突然发现redis set命令的用法可以直接指定nx和ex,文档中没...

    用户1225216
  • Extjs4.2+webAPI+EF实现分页以及webapi的数据传值

    由于不明白分页的总数是怎么计算,不知道他的分页方式所以花费了好多功夫,现在弄出来了与大家分享下 1.首先是EF的简历,想必大家都清楚:添加-〉新建项-〉数据-〉...

    hbbliyong

扫码关注云+社区

领取腾讯云代金券