使用带有QWinWidget的CDialog上的托管Qt小部件时,键盘输入无法正常工作

Keyboard input doesn't work properly when using hosting Qt widget on CDialog with QWinWidget

本文关键字:输入 键盘 常工作 工作 QWinWidget CDialog 小部 Qt      更新时间:2023-10-16

我逐渐将应用程序从MFC迁移到Qt,并为此目的使用MFCMigrationFramework

Qt小部件放置在MFC对话框上不处理诸如Tab箭头EnterEsc等键。这个解决方案部分解决了制表符和箭头的问题:

我已经子类化了QWinWidget并做了下面的事情:构造函数:

SetWindowLong(winId(), GWL_STYLE, GetWindowLong(winId(), GWL_STYLE) | WS_TABSTOP);

重载winEvent:

bool winEvent(MSG *msg, long *result)
{
  switch(msg->message)
  {
    case WM_GETDLGCODE:
      *result = DLGC_WANTARROWS | DLGC_WANTTAB;
      return true;
  }
  return __super::winEvent(msg, result);
}

要复制到剪贴板,将视图切换到纯文本模式

它的工作,除了一个问题:这是不可能达到控制在父对话框(MFC)使用Tab键,焦点周期仅通过子Qt控制(第一个问题)。

第二个问题: 输入Esc键仅由父MFC对话框处理。例如,它是不可能关闭打开的弹出框的组合框(位于Qt小部件)按Enter或Esc键-对话框被关闭而不是(CDialog::OnOKCDialog::OnCancel被调用)。

我试过了

case WM_GETDLGCODE:
  *result = DLGC_WANTARROWS | DLGC_WANTTAB | DLGC_WANTALLKEYS;

但是在这种情况下CDialog不再处理Esc和Enter键。

处理这种情况的正确解决方案是什么?

关于你的第一个问题:对我来说有效的是根据当前的焦点来决定你是否处于焦点链中的"端点"。如果您在末尾得到一个制表符(或者在开头得到一个shift制表符),那么不要在返回值中包含DLGC_WANTTAB:

else if (msg->message == WM_GETDLGCODE)
{
  // Initialize our result, as we always want arrows
  *result = DLGC_WANTARROWS;
  // Check to see if we want tabs
  if (msg->wParam == VK_TAB)
  {
    // Was this a tab or a backtab?
    QWidget *pFocusChainEndpoint = NULL;
    if (::GetKeyState(VK_SHIFT) < 0)
    {
      pFocusChainEndpoint = m_pFirstTabStop;
    }
    else
    {
      pFocusChainEndpoint = m_pFinalTabStop;
    }
    // Determine our current-focusing widget
    QWidget *pCurrent = focusWidget();
    if (pCurrent == NULL)
    {
      // We have no focus, so we don't want the tab event
      return true;
    }
    // If we are *not* at a relevant endpoint in the focus chain,
    // we want to handle the tab event
    if (pCurrent != pFocusChainEndpoint)
    {
      *result |= DLGC_WANTTAB;
    }
  }
  // This message has been handled
  return true;
}

m_pFirstTabStopm_pFinalTabStopshow()代码中确定:

  // Determine the first link in our focus chain
  m_pFirstTabStop = nextInFocusChain();
  Q_ASSERT(m_pFirstTabStop != NULL);
  if (m_pFirstTabStop == NULL)
  {
    // We have no endpoint
    return;
  }
  QString qstrChainObjectName = m_pFirstTabStop->objectName();
  while (!(m_pFirstTabStop->focusPolicy() & Qt::TabFocus)
    || qstrChainObjectName.left(3) == "qt_")
  {
    m_pFirstTabStop = m_pFirstTabStop->nextInFocusChain();
    if (m_pFirstTabStop == this || m_pFirstTabStop == NULL)
    {
      // We've looped through them all, and none require focus --- perhaps the
      // control is just labels --- so we have a NULL tab stop
      m_pFirstTabStop = NULL;
      return;
    }
    qstrChainObjectName = m_pFirstTabStop->objectName();
  }
  // Determine the last link in our focus chain
  m_pFinalTabStop = previousInFocusChain();
  Q_ASSERT(m_pFinalTabStop != NULL);
  if (m_pFinalTabStop == NULL)
  {
    // We have no endpoint
    return;
  }
  qstrChainObjectName = m_pFinalTabStop->objectName();
  while (!(m_pFinalTabStop->focusPolicy() & Qt::TabFocus)
    || qstrChainObjectName.left(3) == "qt_")
  {
    m_pFinalTabStop = m_pFinalTabStop->previousInFocusChain();
    if (m_pFinalTabStop == this || m_pFinalTabStop == NULL)
    {
      // We've looped through them all, and none require focus --- perhaps the
      // control is just labels --- so we have a NULL tab stop
      m_pFinalTabStop = NULL;
      return;
    }
    qstrChainObjectName = m_pFinalTabStop->objectName();
  }

虽然我没有测试这个案例,它应该能够正确地处理只有非表格控件的UI(例如,只有QLabels)