Создание окна, размер которого меняется лишь в одном направлении


Автор: Рэймонд Чен.
Оригинал статьи: Creating a window that can be resized in only one direction.

Сегодняшняя программка демонстрирует окно, размер которого меняется лишь в одном направлении, например, по вертикали.

Внесите следующие правки в шаблон программы:

UINT OnNcHitTest(HWND hwnd, int x, int y)
{
 UINT ht = FORWARD_WM_NCHITTEST(hwnd, x, y, DefWindowProc);
 switch (ht) {
 case HTBOTTOMLEFT:  ht = HTBOTTOM; break;
 case HTBOTTOMRIGHT: ht = HTBOTTOM; break;
 case HTTOPLEFT:     ht = HTTOP;    break;
 case HTTOPRIGHT:    ht = HTTOP;    break;
 case HTLEFT:        ht = HTBORDER; break;
 case HTRIGHT:       ht = HTBORDER; break;
 }
 return ht;
}
...
HANDLE_MSG(hwnd, WM_NCHITTEST, OnNcHitTest);

Мы убираем возможность изменения размера по горизонтали для левой и правой границ, а также углов окна. Для углов мы запрещаем изменять ширину, но разрешаем изменять высоту. Для левой и правой сторон менять размер запрещено, поскольку они должны вести себя как инертные границы.

Но это ещё не всё. Приведённый код обрабатывает изменение размеров мышью, однако не мешает пользователю нажать Alt+Space, S (for Size), затем клавишу со стрелкой влево или вправо.

Нам требуется обрабатывать сообщение WM_GET­MIN­MAX­INFO.

void OnGetMinMaxInfo(HWND hwnd, LPMINMAXINFO lpmmi)
{
 RECT rc = { 0, 0, 500, 0 };
 AdjustWindowRectEx(&rc, GetWindowStyle(hwnd), FALSE,
                    GetWindowExStyle(hwnd));

 // задаём ширину
 lpmmi->ptMaxSize.x =
 lpmmi->ptMinTrackSize.x =
 lpmmi->ptMaxTrackSize.x = rc.right - rc.left;
}
...
HANDLE_MSG(hwnd, WM_GETMINMAXINFO, OnGetMinMaxInfo);

Это работает замечательно, кроме случая с разворачиванием на втором экране, когда окно помещается по горизонтали, но его высота больше высоты экрана.

void OnGetMinMaxInfo(HWND hwnd, LPMINMAXINFO lpmmi)
{
   RECT rc = { 0, 0, 500, 0 };
   AdjustWindowRectEx(&rc, GetWindowStyle(hwnd), FALSE,
                      GetWindowExStyle(hwnd));

   // задаём ширину
   lpmmi->ptMaxSize.x =
   lpmmi->ptMinTrackSize.x =
   lpmmi->ptMaxTrackSize.x = rc.right - rc.left;

   // задаём высоту
   MONITORINFO mi = { sizeof(mi) };
   GetMonitorInfo(MonitorFromWindow(hwnd,
                      MONITOR_DEFAULTTOPRIMARY), &mi);
   lpmmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top
                      - lpmmi->ptMaxPosition.y + rc.bottom;
}

Здесь немного хитрая математика. Мы хотим, чтобы высота окна равнялась высоте рабочей области монитора, на котором расположено окно, плюс  некоторое пространство, чтобы «спрятать» границы окна за пределами экрана.

Выражение mi.rcWork.bottom - mi.rcWork.top вычисляет высоту рабочей области.

Далее мы хотим добавить высоту границ окна за верхним краем экрана. К счастью, диспетчер окон точно сообщает, насколько окно выступает за верхний край экрана: это lpmmi->ptMaxPosition.y с отрицательным значением, так как это координата за верхней границей экрана. Мы должны использовать её с противоположным знаком.

Наконец, мы добавляем размер границ окна за пределами нижнего края рабочей области.

Обработка этой ситуации (с частичными ограничениями окна) утомительна. Сожалею.

Реклама
Запись опубликована в рубрике перевод, программирование с метками , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s