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

你們知道,在使用微軟的編程環境建立工程時會讓你選擇是控制檯模式仍是Windows應用程序。若是選擇控制檯的console模式,就會在運行時出現一個黑洞洞的字符模式窗口,裏面就有等待輸入一閃一閃的插入符。輸入光標從DOS時代就存在,可是在Win32中賦予了更強大的功能。下圖就是Windows的CMD窗口,其中的輸入點就是插入光標:java

cmd_window

要注意的是這裏的插入符或插入光標並非Windows中另一個「光標」,這裏是指示插入字符的位置,而不是用於鼠標,手寫輸入等能夠定位、移動的光標(Cursor),而是插入符Caret,本文也成爲插入光標,注意插入二字,爲了方便,如下在本文中也簡稱爲光標或插入符,但要注意此光標非彼光標。程序員

爲何會有插入光標(插入符)?瞭解了這個基本問題,就成功了一半了。咱們知道計算機能夠經過鍵盤來輸入各類字符和控制符,那麼天然就存在一個問題,輸入的字符應該放到屏幕的什麼位置?這個就是光標產生的緣由,光標實際上就是一個字符插入標識。簡單的說就是咱們想把字符輸入到哪一個位置,就先把插入符設置到那裏,設置時其實就是告訴電腦輸入的字符你給我在哪裏顯示!從這個咱們也能夠推斷,插入符在同一時刻只能有一個。編程

  • 光標相關API函數小程序

要使用光標,首先得建立一個光標,建立光標的API函數爲:windows

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

hWnd參數表示光標是屬於哪一個窗口。微信

hBitmap參數是一個位圖的句柄,計算機將使用這個句柄的位圖來做爲光標的形狀。app

既然光標是給使用電腦的人插入字符用的,那就得有形狀讓使用者能看到,所以光標須要有一個可見的小圖標。固然爲了避免同的狀況和需求,Windows讓咱們能夠自定義光標的形狀。常見的位圖建立或者加載API函數如CreateBitmap、CreateDIBitmap、LoadBitmap等均可以建立或加載一個位圖,將句柄做爲該參數。微信公衆平臺

nWidth和nHeight分別是位圖的寬和高。ide

光標建立以後是不會自動顯示的,也就是默認是隱藏狀態,須要主動調用下面的顯示函數:函數

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 TCHAR szWinName[] = TEXT("VirtualTerminal");
#define TEXTMATRIX(x, y)  *(pTextMatrix + ((y) * nLineChars) + (x))
static TEXTMETRIC tm; //metric dimention of virtual terminal
static TCHAR *pTextMatrix; //VT character buffer
static int nCharWidth, nCharHeight; //the width and height of characeters
static int nCaretPosX, nCaretPosY; //the current position of caret
static int nVTWidth, nVTHeight; //the width and height of virtual terminal window
static int nLineChars, nRowChars; //character number in one line and row
static int nCaretOffsetY; //the offset of vertical height
static COLORREF TextColor; //text color for terminal window, that is fore color

static LRESULT CALLBACK VTWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

HWND CreateVirtualTerminal(HWND hWndParent, int left, int top, int width, int height, int id)
{
    HWND hWnd;
    WNDCLASS wndclass;

    HINSTANCE hInst = GetModuleHandle(NULL);
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = VTWndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = hInst;
    wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = szWinName;
    if (!RegisterClass(&wndclass))
    {
        return 0;
    }
    hWnd = CreateWindowEx(0, szWinName, NULL, WS_CHILD|WS_VISIBLE, 
                        left, top, width, height, hWndParent, (HMENU)id, hInst, NULL);
    return hWnd;
}

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:       // Home 
            nCaretPosX = 0;
            break; 

        case VK_END:        // End 
            nCaretPosX = nLineChars - 1;
            break; 

        case VK_PRIOR:      // Page Up 
            nCaretPosY = 0; 
            break; 

        case VK_NEXT:       // Page Down 
            nCaretPosY = nRowChars -1; 
            break; 

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

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

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

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

        case VK_DELETE:     // Delete 
            // Move all the characters that followed the 
            // deleted character (on the same line) one 
            // space back (to the left) in the matrix. 
             for (x = nCaretPosX; x < nLineChars; x++) 
                TEXTMATRIX(x, nCaretPosY) = TEXTMATRIX(x + 1, nCaretPosY); 
            // Replace the last character on the 
            // line with a space. 
            TEXTMATRIX(nLineChars - 1, nCaretPosY) = ' '; 
            // The application will draw outside the 
            // WM_PAINT message processing, so hide the caret. 
            HideCaret(hWnd); 
            // Redraw the line, adjusted for the 
            // deleted character. 
            hDC = GetDC(hWnd); 
            DrawChar(hDC, nCaretPosX * nCharWidth, nCaretPosY * nCharHeight,
                    &TEXTMATRIX(nCaretPosX, nCaretPosY), nLineChars - nCaretPosX/nCharWidth);
            ReleaseDC(hWnd, hDC); 

            // Display the caret. 
            ShowCaret(hWnd); 
        break; 
        } 
        // Adjust the caret position based on the 
        // virtual-key processing. 
        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: //process a backspace.
            if (nCaretPosX > 0)
            {
                nCaretPosX--;
                SendMessage(hWnd, WM_KEYDOWN, VK_DELETE, 1L);
            }
            break;
        case 0x09: //process a tab.
            do
            {
                SendMessage(hWnd, WM_CHAR, TEXT(' '), 2L);
            } while (nCaretPosX % 4 != 0);
            break;
        case 0x0D: //process a carriage return.
            nCaretPosX = 0;
            if (++nCaretPosY == nRowChars)
            {
                nCaretPosY = 0;
            }
            break;
        case 0x1B:
        case 0x0A: //process a linefeed.
            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函數來建立一個窗口。

#include <windows.h>

#define IDC_VTID  9876
static TCHAR szAppName[] = TEXT("Caret demo");
static LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
extern HWND CreateVirtualTerminal(HWND hWndParent, int left, int top, int width, int height, int id);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
     HWND     hWnd;
     MSG      msg;
     WNDCLASS wndclass;

     wndclass.style         = CS_HREDRAW | CS_VREDRAW;
     wndclass.lpfnWndProc   = WndProc;
     wndclass.cbClsExtra    = 0;
     wndclass.cbWndExtra    = 0;
     wndclass.hInstance     = hInstance;
     wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
     wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
     wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
     wndclass.lpszMenuName  = NULL;
     wndclass.lpszClassName = szAppName;

     if (!RegisterClass(&wndclass))
     {
          MessageBox (NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
          return 0;
     }
     
     hWnd = CreateWindow(szAppName,             // window class name
                          szAppName,            // window caption
                          WS_OVERLAPPEDWINDOW,  // window style
                          CW_USEDEFAULT,        // initial x position
                          CW_USEDEFAULT,        // initial y position
                          400,              // initial x size
                          300,              // initial y size
                          NULL,                 // parent window handle
                          NULL,                 // window menu handle
                          hInstance,            // program instance handle
                          NULL);                // creation parameters
     
     ShowWindow(hWnd, iCmdShow);
     UpdateWindow(hWnd);
     
     while (GetMessage(&msg, NULL, 0, 0))
     {
          TranslateMessage(&msg);
          DispatchMessage(&msg);
     }

     return msg.wParam;
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC         hDC;
    PAINTSTRUCT ps;

    switch (message)
    {
    case WM_CREATE:
        CreateVirtualTerminal(hWnd, 10, 10, 360, 240, IDC_VTID);
        return 0;

    case WM_PAINT:
        hDC = BeginPaint(hWnd, &ps);
        ;
        EndPaint(hWnd, &ps);
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0 ;
    }

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

示例程序運行後,在窗口中輸入部分文本(模仿上面的cmd窗口^_^),效果以下:

my_cmd

本例實現了一個簡單的終端模擬小程序,爲了讀者重用方便,我將終端模擬的小窗口單獨做爲一個完整的源文件,而且把窗口背景設爲黑色,前景色設爲白色,看起來更像CMD、Linux等命令行窗口。

 更多經驗交流能夠加入Windows編程討論QQ羣454398517


關注微信公衆平臺:程序員互動聯盟(coder_online),你能夠第一時間獲取原創技術文章,和(java/C/C++/Android/Windows/Linux)技術大牛作朋友,在線交流編程經驗,獲取編程基礎知識,解決編程問題。程序員互動聯盟,開發人員本身的家。

【Windows編程】系列第八篇:建立通用對話框

轉載請註明出處http://www.coderonline.net/?p=1905,謝謝合做!

相關文章
相關標籤/搜索