没有鼠标的win32,如何为按钮分配键盘快捷键

win32 without mouse, how to assign keyboard shortcuts to buttons

我用纯 C 语言编写了一个简单的 win32 GUI 应用程序,带有按钮和其他控件,没有 MFC。我想让那些不能使用鼠标的人更容易使用它。

  1. 首先,我的示例代码不响应 Tab 键将焦点从一个按钮移动到另一个按钮。我可以使用鼠标按下 UI 按钮,然后它会变成焦点,我可以使用 Space-bar 激活按钮,但我无法使用 TabShift+Tab 将焦点移动到其他按钮.我该如何解决这个问题?

  2. 我想为按钮分配键盘提示(小下划线),以便用户可以使用键盘快捷键激活按钮。

我有 google 它,但答案很难 google,所以我需要有人指点我一些文档。一小段代码会很有帮助。

这是我的代码。我使用 WINE + TinyCC

在 Linux 上编译并 运行 它

#include <stdio.h>
#include <windows.h>

/* Yeres distributed forums node v0001 */

/* compile: wine tcc_win/tcc/tcc.exe win_yeres */


HWND status_text_hwnd = NULL;
HWND shutdown_hs_button_hwnd = NULL;

unsigned int button_height = 26;
unsigned int window_length = 400;
#define V_BUTTON_PADDING 2

int tor_running = 0;
int select_running = 0;


#include "yeres_bind.c"
#include "win_yeres_w1_proc.c"


int APIENTRY WinMain(
        HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpCmdLine,
        int nCmdShow
        ) {

  MSG msg;
  WNDCLASS wc;
  HWND hwnd;

  printf("\n  **  YERES v0001  **\n\n");


  // Fill in window class structure with parameters that describe
  // the main window.

  ZeroMemory(&wc, sizeof wc);
  wc.hInstance     = hInstance;
  wc.lpszClassName = "yeres_wcl";
  wc.lpfnWndProc   = (WNDPROC)w1_proc;
  wc.style         = CS_DBLCLKS|CS_VREDRAW|CS_HREDRAW;
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 0);
  wc.hIcon         = LoadIcon(NULL, IDI_EXCLAMATION);
  wc.hCursor       = LoadCursor(NULL, IDC_ARROW);

  if (FALSE == RegisterClass(&wc)) return 0;

  RECT client_rect;
  client_rect.left = 0;
  client_rect.top = 0;
  client_rect.right = window_length;
  client_rect.bottom = 500;

  /* calculate window size from known client area size */
  if (0 == AdjustWindowRect(&client_rect, wc.style, 0)) return 0;

  // create main window
  hwnd = CreateWindow(
    "yeres_wcl",
    "YERES control panel",
    WS_OVERLAPPEDWINDOW|WS_VISIBLE,
    30,
    30,
    client_rect.right,   //CW_USEDEFAULT
    client_rect.bottom,
    0,
    0,
    hInstance,
    0);

  if (NULL == hwnd) return 0;

  // Main message loop:
  while (GetMessage(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return msg.wParam;
}

#define ID_TOR_CONTROL_PORT 1
#define ID_BIND 2
#define ID_UNBIND 3
#define ID_TOR_SHUTDOWN_HS 4
#define ID_PORT_NUM 5
#define ID_QUIT 6


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

  int y = 4;
  int x;
  RECT rc;
  switch (message) {

    case WM_CREATE:

    ZeroMemory(&rc, sizeof(rc));
    GetClientRect(hwnd, &rc);
    if (rc.right) window_length = rc.right - rc.left;

    status_text_hwnd = CreateWindow("Static",
                "** YERES v0001 **\n\nPress 'Bind to localhost'..",
                WS_CHILD | WS_VISIBLE | SS_CENTER,
                3, y, window_length, (button_height * 3),
                hwnd, (HMENU) 1, NULL, NULL);
    y += button_height * 3;

    x = 2;
    CreateWindow("Static", "Port:",
                WS_CHILD | WS_VISIBLE | SS_CENTER,
                x, y, 60, button_height,
                hwnd, (HMENU) 1, NULL, NULL);
    x += 60;
    /* editable field here */
    CreateWindow("Edit", "19407", 
                WS_CHILD | WS_VISIBLE | WS_BORDER,
                x, y, 90, button_height, hwnd, (HMENU) ID_PORT_NUM,
                NULL, NULL);
    x += 120;

    CreateWindow("Button", "Bind to localhost",
                WS_VISIBLE | WS_CHILD ,
                x, y, 140, button_height,
                hwnd, (HMENU) ID_BIND, NULL, NULL);
    x += 140;
    CreateWindow("Button", "Unbind",
                WS_VISIBLE | WS_CHILD ,
                x, y, window_length - 2 - x, button_height,
                hwnd, (HMENU) ID_UNBIND, NULL, NULL);

    y += button_height + V_BUTTON_PADDING;

    CreateWindow("Button", "Use tor control port to start HiddenService",
                WS_VISIBLE | WS_CHILD ,
                2, y, window_length - 4, button_height,
                hwnd, (HMENU) ID_TOR_CONTROL_PORT, NULL, NULL);
    y += button_height + V_BUTTON_PADDING;

    shutdown_hs_button_hwnd = CreateWindow("Button", "Shutdown HiddenService",
                WS_VISIBLE | WS_CHILD ,
                2, y, window_length - 4, button_height,
                hwnd, (HMENU) ID_TOR_SHUTDOWN_HS, NULL, NULL);
    EnableWindow(shutdown_hs_button_hwnd, 0); /* inactive button */
    y += button_height + V_BUTTON_PADDING;

    CreateWindow("Button", "Shutdown EVERYTHING and QUIT",
                WS_VISIBLE | WS_CHILD ,
                2, y, window_length - 4, button_height,
                hwnd, (HMENU) ID_QUIT, NULL, NULL);
    y += button_height + V_BUTTON_PADDING;

    break;

    case WM_DESTROY:
    if (!tor_running && !select_running) PostQuitMessage(0);
    break;


    case WM_COMMAND:
    switch (LOWORD(wParam)) {
      case ID_BIND:
      yeres_bind();
      break;

      case ID_QUIT:
      PostQuitMessage(0);
      break;

      default: DefWindowProc(hwnd, message, wParam, lParam);
    }
    break;

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

  };
  return 0;
};

P.S。抱歉我的英语不好:)

很简单。在主消息处理循环中,我用适当的 HWND 调用 IsDialogMessage()。然后,如果此函数 returns 为零,我将调用正常的 TranslateMessage 和 DispatchMessage 函数。这是代码:

while(GetMessage(&msg, NULL, 0, 0) > 0) {
  if(!IsDialogMessage(hwnd, &msg)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  };

  yeres_select(); /* scan incoming connections on sockets using select(), activated by timer and/or other messages */

}

window 中的每个按钮都设置了位 WS_TABSTOP,因此当我按下 Tab 时,IsDialogMessage() 将捕获消息并将焦点移动到下一个按钮。 Editable也可以这样选择。

WS_GROUP 对我不起作用。理论上它应该允许我使用箭头键进行导航。我在第一个按钮中放置了 WS_GROUP 位。

CreateWindow("Button", "&Bind to localhost",
            WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP,
            x, y, 140, button_height,
            hwnd, (HMENU) ID_BIND, NULL, NULL);

即使没有 TranslateAccelerator() 调用,符号助记符也可以工作,只需将其放入标签文本 "&Bind to localhost",并且可以使用 Alt+B 左右激活按钮。不区分大小写。

我没有在 Windows 上检查它,但我认为如果它在 WINE 中工作应该没问题。