Win32 Edit Control Смещение размещения каретки

Я дал объяснение своей проблемы на английском языке ниже, но это визуальная проблема, поэтому, если вы не хотите читать все это, просто посмотрите на картинку внизу).

Я работаю над созданием калькулятора обратного полирования для своего класса, и я только что закончил, чтобы элементы управления кнопками в моем графическом интерфейсе могли добавлять свои значения в элемент управления редактированием, который отлично работает, но курсор делает что-то странное, и я могу' t найти любую информацию о нем.

Я отправляю пользовательское сообщение в элемент управления редактированием, в котором он находит длину текущего текста в элементе управления, а затем помещает курсор в конец текста, чтобы я мог затем добавить текст, который нужно добавить (он выровнен по правому краю с 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 );
}

Изображение проблемы:

изображение


person user3010974    schedule 31.05.2014    source источник
comment
index = new int( GetWindowTextLength (hWnd) ); создает указатель на int, который затем просачивается, и, вероятно, в любом случае это не то, что вы хотели. Где декларация для index?   -  person Adrian McCarthy    schedule 17.11.2015


Ответы (2)


Это может быть или не быть причиной, но вы неправильно используете 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, чтобы расширить правый край элемента управления прямоугольник форматирования на несколько пикселей. Это должно дать каретке дополнительное пространство для работы.

person Remy Lebeau    schedule 31.05.2014
comment
Думаю, я не вставил весь соответствующий код, потому что у меня есть понитер, хранящийся в классе, который я создал в своей структуре. Указатель не вызывает утечки памяти, потому что он существует, пока существует объект, и деструктор объекта очищает его. Я передаю ему разыменованный указатель, потому что значение, на которое он указывает, является размером текста текущего окна. Я настроил его как указатель, поэтому мне не нужно его инициализировать во время создания объекта, но я попробую изменить его на переменную и посмотреть, сработает ли это. Я полагаю, что расширение прямоугольника, скорее всего, сработает. - person user3010974; 01.06.2014
comment
Я пытался сделать индекс прямым int вместо int *, отправив EM_SETRECT после редактирования возвращенного RECT из EM_GETRECT и вручную отрегулировав поля с помощью EM_SETMARGINS. Я видел, что изменения вступили в силу, поэтому я знаю, что они были реализованы правильно, но крайняя правая позиция каретки по-прежнему находится прямо в середине числа 4, а также нескольких других чисел. Думаю, мне просто нужно смириться с этим. - person user3010974; 01.06.2014
comment
У вас есть утечка памяти, потому что вы выделяете новый int каждый раз, когда получаете сообщение WM_APPEND_TEXT, и не освобождаете предыдущее int. Но вопрос спорный, так как вы не должны использовать указатель в первую очередь, потому что EM_SETSEL не поддерживает указатели int*. - person Remy Lebeau; 01.06.2014
comment
Справедливо, но по какой-то причине функция работает с int*. Но да, это спорный вопрос, потому что я также пробовал это только с переменной int после вашего первого ответа, и это ничего не изменило. Как я сказал в своем втором ответе, я пытался настроить размер прямоугольника, размер поля, вручную поместить каретку и использовать как int, так и int * для размещения каретки, но ничто не решает проблему. Каждый испробованный метод по-прежнему будет оставлять самое правильное место, которое может занимать каретка, лежащее непосредственно над любым символом, который находится в самой правой позиции символа. - person user3010974; 01.06.2014

Столкнувшись с той же проблемой и представив свой первый подход в этом ответе, я теперь предоставлю два хорошо работающих решения. Я думаю, что нет другого способа правильно исправить этот сбой (если вы не программист Microsoft, который отвечает за эту часть WinAPI).

Мне было интересно, как исправить эту проблему с элементами управления редактированием, созданными с помощью ES_MULTILINE, но этот сбой, похоже, является проблемой только для однострочных элементов управления редактированием (проверено на 64-разрядной версии Windows 7). Включение визуальных стилей также полезно, но проблема все еще остается (смещение, по крайней мере, не так очевидно).

Пояснение

Обычно, когда курсор находится в самом дальнем правом положении, его значение x (предоставленное GetCaretPos ()) должно быть равно значению rect.right, предоставленному EM_GETRECT (когда элемент управления редактированием был создан с помощью ES_RIGHT). По неизвестным причинам это не так. Таким образом, вы должны проверить, находится ли позиция каретки по крайней мере рядом со значением rect.right (но не дальше, чем ширина последней буквы). Таким образом, у вас есть две возможности выполнить эту задачу:

  1. Вы должны рассчитать ширину внешнего правого символа, используя GetTextExtentPoint32 (), вычесть его из значения rect.right, предоставленного вызовом SendMessage () с EM_GETRECT, и проверить, больше ли позиция x каретки, чем результат или нет ИЛИ
  2. Вы должны рассчитать границу между значением 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 () гарантирует, что курсор изменится при наведении мыши.

person user5569606    schedule 16.11.2015
comment
В документации указано EM_GETRECT, EM_SETRECT и EM_SETRECTNP применяется только к многострочным элементам управления. Он также говорит, что EM_GETRECT является приблизительным и может быть отключен на несколько пикселей. - person Adrian McCarthy; 10.05.2021