Смещение размещения элемента управления каретки Win32
Я дал английское объяснение моей проблемы ниже, но это визуальная проблема, поэтому, если вы не хотите читать все это, просто посмотрите на картинку внизу).
Я работаю над созданием калькулятора обратной польской нотации для моего класса, и я только что закончил, чтобы элементы управления кнопок в моем графическом интерфейсе могли добавлять свои значения в элемент управления редактирования, который работает нормально, но карет делает что-то странное, и я могу ' не могу найти никакой информации об этом.
Я отправляю пользовательское сообщение в элемент управления для редактирования, в котором он находит длину текущего текста в элементе управления, а затем помещает курсор в конец текста, чтобы затем я мог добавить, какой текст необходимо добавить (он выравнивается по правому краю ES_RIGHT
), который снова работает просто отлично, но когда каретка находится в самом правильном месте, это может быть, она помещается практически прямо в середину практически любого числа.
Кажется, что это происходит только в самом правильном месте, где может находиться каретка (то есть где-нибудь еще, каретка находится справа от предыдущего символа, как и должно быть), и я попытался заменить каретку до конца вправо, используя код, поместив его с помощью моей клавиатуры / мыши, и попытался отрегулировать размеры окна в надежде, что это было только смещение ширины, которое я определил для него, из-за которого последнее место слегка сместилось, но проблема сохраняется, и это усложняет задачу читать последний символ в текстовом поле.
Соответствующий код:
LRESULT CALLBACK EditBoxClass::WinProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_COMMAND:
break;
case WM_APPEND_EDIT:
/* Get current length of text in the box */
index = new int( GetWindowTextLength (hWnd) );
SetFocus( hWnd );
/* Set the caret to the end of the text in the box */
SendMessage( hWnd, EM_SETSEL, (WPARAM)index, (LPARAM)index );
/* "Replace" the selection (the selection is actually targeting
nothing and just sits at the end of the text in the box)
with the passed in TCHAR* from the button control that
sent the WM_APPEND_EDIT message */
SendMessage( hWnd, EM_REPLACESEL, 0, lParam );
break;
}
return CallWindowProc( EditClassStruct.GetOldProc(), hWnd, msg, wParam, lParam );
}
Изображение проблемы:
2 ответа
Столкнувшись с той же проблемой и представив свой первый подход в этом ответе, я теперь предоставлю два хорошо работающих решения. Я думаю, что нет другого способа исправить этот глюк должным образом (если вы не программист Microsoft, который отвечает за эту часть WinAPI).
Мне было интересно, как исправить эту проблему на элементах управления редактирования, созданных с помощью ES_MULTILINE
но этот сбой, по-видимому, является проблемой только для однострочных элементов управления редактированием (протестировано в Windows 7 64-bit). Включение визуальных стилей также полезно, но проблема все еще остается (смещение, по крайней мере, не так очевидно).
объяснение
Обычно, когда каретка находится в крайнем правом положении, это значение х (предоставляется GetCaretPos ()
) должен быть равен rect.right
значение предоставлено EM_GETRECT
(когда элемент управления редактирования был создан с ES_RIGHT
). По неизвестным причинам это не так. Таким образом, вы должны проверить, находится ли позиция каретки, по крайней мере, вблизи rect.right
значение (но не дальше, чем широкая последняя буква). Таким образом, у вас есть две возможности выполнить эту задачу:
- Вы должны вычислить ширину внешнего правого символа, используя
GetTextExtentPoint32 ()
вычтите это изrect.right
значение, предоставляемое звонкомSendMessage ()
сEM_GETRECT
и проверьте, больше ли позиция х каретки, чем результат, или нет ИЛИ - Вы должны рассчитать разницу между
rect.right
значение и внешнее правое положение каретки (3
в моем случае) и использовать это значение как жестко заданное смещение, чтобы выполнить простую проверку.
После этих шагов (независимо от того, какой из них вы выбрали) вы должны изменить положение каретки, когда это необходимо.
1. Подход (рекомендуется)
case WM_LBUTTONDOWN: {
TRACKMOUSEEVENT tme = {sizeof (tme), TME_LEAVE, hwnd, HOVER_DEFAULT};
TrackMouseEvent (&tme);
}
break;
case WM_KEYDOWN:
case WM_MOUSELEAVE:
case WM_SETCURSOR: {
DefSubclassProc (hwnd, message, wParam, lParam);
DWORD end;
SendMessage (hwnd, EM_GETSEL, (WPARAM) NULL, (LPARAM) &end);
int len = GetWindowTextLength (hwnd);
if (end < len || len <= 0)
return TRUE;
wchar_t *buffer = new wchar_t[len + 1];
GetWindowText (hwnd, buffer, len + 1);
wchar_t lastChar[] = {buffer[len - 1], '\0'};
delete[] buffer;
SIZE size;
HDC hdc = GetDC (hwnd);
if (hdc == NULL)
return TRUE;
GetTextExtentPoint32 (hdc, lastChar, 1, &size);
ReleaseDC (hwnd, hdc);
POINT pt;
RECT rect;
GetCaretPos (&pt);
SendMessage (hwnd, EM_GETRECT, (WPARAM) 0, (LPARAM) &rect);
if ((rect.right - size.cx) <= pt.x)
SetCaretPos (rect.right, pt.y);
return TRUE;
}
break;
2. Подход (улучшенная оригинальная версия)
case WM_LBUTTONDOWN: {
TRACKMOUSEEVENT tme = {sizeof (tme), TME_LEAVE, hwnd, HOVER_DEFAULT};
TrackMouseEvent (&tme);
}
break;
case WM_KEYDOWN:
case WM_MOUSELEAVE:
case WM_SETCURSOR: {
DefSubclassProc (hwnd, message, wParam, lParam);
POINT pt;
RECT rect;
GetCaretPos (&pt);
SendMessage (hwnd, EM_GETRECT, (WPARAM) 0, (LPARAM) &rect);
if ((rect.right - pt.x) <= 3)
SetCaretPos (rect.right, pt.y);
return TRUE;
}
break;
Вы должны разделить элементы управления на подклассы. Затем используйте этот код в своих оконных процедурах и наслаждайтесь. В обоих случаях отслеживание события мыши не является абсолютно необходимым, но рекомендуется полностью избегать этого сбоя. призвание DefSubclassProc ()
будет гарантировать, что курсор будет изменен при наведении мыши.
Это может или не может быть причиной, но вы злоупотребляете EM_SETSEL
, Вы динамически распределяете (и просачиваете) int
в куче и передавая указатель на него в качестве параметров сообщения, но EM_SETSEL
не ожидает и не использует указатели для начала. Так что избавьтесь от динамического распределения.
Кроме того, процедура окна по умолчанию не будет знать, как обрабатывать ваши WM_APPEND_EDIT
сообщение, поэтому нет смысла передавать сообщение CallWindowProc()
,
Попробуйте это вместо этого:
case WM_APPEND_EDIT:
{
/* Get current length of text in the box */
int index = GetWindowTextLength( hWnd );
SetFocus( hWnd );
/* Set the caret to the end of the text in the box */
SendMessage( hWnd, EM_SETSEL, (WPARAM)index, (LPARAM)index );
/* "Replace" the selection (the selection is actually targeting
nothing and just sits at the end of the text in the box)
with the passed in TCHAR* from the button control that
sent the WM_APPEND_EDIT message */
SendMessage( hWnd, EM_REPLACESEL, 0, lParam );
return 0;
}
Как говорится, попробуйте использовать EM_GETRECT
/ EM_SETRECT
расширить правый край прямоугольника форматирования элемента управления для редактирования на несколько пикселей. Это должно дать карете дополнительное пространство для работы.