1
votes

I'm gradually migrating application from MFC to Qt and using MFCMigrationFramework for this purpose.

Qt widget which is placed on MFC dialog doesn't handle such keys as Tab, Arrows, Enter, Esc. Issue with Tab and arrows was partially solved with this solution:

I've subclassed QWinWidget and have made next things: Constructor:

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

Overriden 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);
}

To copy to clipboard, switch view to plain text mode

It works except one issue: it's impossible to reach controls on parent dialog (MFC) using Tab key, focus cycles only through child Qt control (the first issue).

The second issue: Enter and Esc keys are handled only by parent MFC dialog. For instance, it's impossible to close opened popup of combobox (located on Qt widget) by pressing Enter or Esc key - dialog is closed instead (CDialog::OnOK or CDialog::OnCancel is called).

I've tried this

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

but in this case CDialog doesn't handle Esc and Enter keys anymore.

What is the right solution to handle such situation?

1

1 Answers

2
votes

With regard to your first issue: what worked for me is to decide, based on the current focus point, if you're at an "endpoint" in the focus chain. If you are at the end and get a tab (or at the beginning and get a shift-tab), then don't include DLGC_WANTTAB in your return value:

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_pFirstTabStop and m_pFinalTabStop are determined in the show() code:

  // 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();
  }

Though I didn't test this case, it should be able to properly handle a UI with only non-tabbable controls (e.g. just QLabels).