Blog. Just Blog

Магнитное окно с учетом нескольких мониторов

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Продолжаем работу над ошибками. Несколько лет назад в одной из статей я рассказывал, как сделать окно, которое при перемещении будет прилипать ко краям экрана. Этот способ рабочий, но он не учитывает ситуацию, когда у пользователя более одного монитора. Дело в том, что функция SystemParametersInfo с параметром SPI_GETWORKAREA возвращает координаты рабочей области только основного монитора. Для поддержки нескольких мониторов, в том числе произвольных размеров, надо сперва определить, на каком мониторе находится окно. Затем надо будет получить характеристики этого монитора, точнее координаты его рабочей области. Но сперва придется определить структуру и константу, про которую не знает коробочный FASM.
  1. MONITOR_DEFAULTTONEAREST = 2
  2.  
  3. struct MONITORINFO
  4.     cbSize    dd ?
  5.     rcMonitor RECT
  6.     rcWork    RECT
  7.     dwFlags   dd ?
  8. ends 
Теперь немного теории. Чтобы узнать положение окна, надо воспользоваться функцией MonitorFromWindow с флагом MONITOR_DEFAULTTONEAREST, которая, в случае успеха, вернет хэндл монитора, на котором находится указанное окно, или хэндл дефолтного монитора в случае неудачи. Зная этот хэндл, можно получить параметры монитора при помощи функции GetMonitorInfo. В структуру MONITORINFO записываются размеры монитора и размеры его рабочей области, на основании которых мы и будем работать.

В остальном код очень похож на предыдущий пример. Точно так же обрабатывается событие окна WM_MOVING, точно так же задается размер магнитной сетки MAX_GRID. Только для удобства я вынес проверку и корректировку координат окна в отдельную процедуру:
  1. ;---------------------------------------------------------------------
  2. ; Корректировка координат окна с учетом положения относительно
  3. ; границ монитора, на котором он находится
  4. ;---------------------------------------------------------------------
  5. ; Параметры:
  6. ;     hwnd  - хэндл окна
  7. ;     coord - координаты окна
  8. ;     grid  - размер магнитной сетки
  9. ;---------------------------------------------------------------------
  10. proc StickyWindow hwnd:DWORD, coord:DWORD, grid:DWORD
  11.  
  12.         MONITOR_DEFAULTTONEAREST = 2
  13.  
  14.         struct MONITORINFO
  15.           cbSize    dd ?
  16.           rcMonitor RECT
  17.           rcWork    RECT
  18.           dwFlags   dd ?
  19.         ends
  20.  
  21.         locals
  22.           minfo MONITORINFO
  23.         endl
  24.  
  25.         pusha
  26.  
  27.         ; Монитор, на котором находится окно
  28.         invoke  MonitorFromWindow,[hwnd],MONITOR_DEFAULTTONEAREST
  29.  
  30.         ; В регистре ESI указатель на структуру информации о мониторе
  31.         lea     esi,[minfo]
  32.         mov     [esi+MONITORINFO.cbSize],sizeof.MONITORINFO
  33.         invoke  GetMonitorInfo,eax,esi
  34.  
  35.         ; В регистре EDI указатель на структуру координат окна
  36.         mov     edi,[coord]
  37.  
  38.         ; Смещение окна относительно левой границы экрана
  39.         mov     edx,[edi+RECT.left]
  40.         sub     edx,[esi+MONITORINFO.rcWork.left]
  41.         mov     eax,edx
  42.         jns     @f
  43.         neg     edx
  44. @@:
  45.         cmp     edx,[grid]
  46.         ja      @f
  47.         sub     [edi+RECT.right],eax
  48.         sub     [edi+RECT.left],eax
  49. @@:
  50.         ; Смещение окна относительно правой границы экрана
  51.         mov     edx,[edi+RECT.right]
  52.         sub     edx,[esi+MONITORINFO.rcWork.right]
  53.         mov     eax,edx
  54.         jns     @f
  55.         neg     edx
  56. @@:
  57.         cmp     edx,[grid]
  58.         ja      @f
  59.         sub     [edi+RECT.right],eax
  60.         sub     [edi+RECT.left],eax
  61. @@:
  62.         ; Смещение окна относительно верхней границы экрана
  63.         mov     edx,[edi+RECT.top]
  64.         sub     edx,[esi+MONITORINFO.rcWork.top]
  65.         mov     eax,edx
  66.         jns     @f
  67.         neg     edx
  68. @@:
  69.         cmp     edx,[grid]
  70.         ja      @f
  71.         sub     [edi+RECT.top],eax
  72.         sub     [edi+RECT.bottom],eax
  73. @@:
  74.         ; Смещение окна относительно нижней границы экрана
  75.         mov     edx,[edi+RECT.bottom]
  76.         sub     edx,[esi+MONITORINFO.rcWork.bottom]
  77.         mov     eax,edx
  78.         jns     @f
  79.         neg     edx
  80. @@:
  81.         cmp     edx,[grid]
  82.         ja      @f
  83.         sub     [edi+RECT.top],eax
  84.         sub     [edi+RECT.bottom],eax
  85. @@:
  86.         popa
  87.         ret
  88. endp
Если окно обычное, то при расчете координат и размеров окна надо будет дополнительно учитывать толщину рамок. Процедура для таких случаев будет немного дополнена:
  1. ;---------------------------------------------------------------------
  2. ; Корректировка координат окна с учетом положения относительно
  3. ; границ монитора, на котором он находится
  4. ;---------------------------------------------------------------------
  5. ; Параметры:
  6. ;     hwnd  - хэндл окна
  7. ;     coord - координаты окна
  8. ;     grid  - размер магнитной сетки
  9. ;---------------------------------------------------------------------
  10. proc StickyWindow hwnd:DWORD, coord:DWORD, grid:DWORD
  11.  
  12.         MONITOR_DEFAULTTONEAREST = 2
  13.  
  14.         struct MONITORINFO
  15.           cbSize    dd ?
  16.           rcMonitor RECT
  17.           rcWork    RECT
  18.           dwFlags   dd ?
  19.         ends
  20.  
  21.         locals
  22.           minfo MONITORINFO
  23.         endl
  24.  
  25.         pusha
  26.  
  27.         ; Монитор, на котором находится окно
  28.         invoke  MonitorFromWindow,[hwnd],MONITOR_DEFAULTTONEAREST
  29.  
  30.         ; В регистре ESI указатель на структуру информации о мониторе
  31.         lea     esi,[minfo]
  32.         mov     [esi+MONITORINFO.cbSize],sizeof.MONITORINFO
  33.         invoke  GetMonitorInfo,eax,esi
  34.  
  35.         ; Учитывать толщину рамки окна
  36.         invoke  GetSystemMetrics,SM_CXFRAME
  37.         shr     eax,1
  38.         mov     ecx,eax
  39.  
  40.         ; В регистре EDI указатель на структуру координат окна
  41.         mov     edi,[coord]
  42.  
  43.         ; Смещение окна относительно левой границы экрана
  44.         mov     edx,[edi+RECT.left]
  45.         sub     edx,[esi+MONITORINFO.rcWork.left]
  46.         sub     edx,ecx
  47.         mov     eax,edx
  48.         jns     @f
  49.         neg     edx
  50. @@:
  51.         cmp     edx,[grid]
  52.         ja      @f
  53.         sub     [edi+RECT.right],eax
  54.         sub     [edi+RECT.left],eax
  55. @@:
  56.         ; Смещение окна относительно правой границы экрана
  57.         mov     edx,[edi+RECT.right]
  58.         sub     edx,[esi+MONITORINFO.rcWork.right]
  59.         add     edx,ecx
  60.         mov     eax,edx
  61.         jns     @f
  62.         neg     edx
  63. @@:
  64.         cmp     edx,[grid]
  65.         ja      @f
  66.         sub     [edi+RECT.right],eax
  67.         sub     [edi+RECT.left],eax
  68. @@:
  69.         ; Смещение окна относительно верхней границы экрана
  70.         mov     edx,[edi+RECT.top]
  71.         sub     edx,[esi+MONITORINFO.rcWork.top]
  72.         sub     edx,ecx
  73.         mov     eax,edx
  74.         jns     @f
  75.         neg     edx
  76. @@:
  77.         cmp     edx,[grid]
  78.         ja      @f
  79.         sub     [edi+RECT.top],eax
  80.         sub     [edi+RECT.bottom],eax
  81. @@:
  82.         ; Смещение окна относительно нижней границы экрана
  83.         mov     edx,[edi+RECT.bottom]
  84.         sub     edx,[esi+MONITORINFO.rcWork.bottom]
  85.         add     edx,ecx
  86.         mov     eax,edx
  87.         jns     @f
  88.         neg     edx
  89. @@:
  90.         cmp     edx,[grid]
  91.         ja      @f
  92.         sub     [edi+RECT.top],eax
  93.         sub     [edi+RECT.bottom],eax
  94. @@:
  95.         popa
  96.         ret
  97. endp
Процедуры самодостаточные, не требуют для своей работы наличия дополнительных данных. Параметрами передаются hwnd - хэндл окна, для которого надо проверить координаты, coord - текущие координаты окна, полученные из обработчика WM_MOVING или при помощи функции GetWindowRect, а также grid - размер магнитной сетки. Пример обработки:
  1.         ; Обработчик событий окна
  2.         cmp     [msg],WM_MOVING
  3.         je      moving_window
  4.         ...
  5.         ...
  6.         ...
  7. moving_window:
  8.         ; Примагничивание окна при перетаскивании его мышкой
  9.         stdcall StickyWindow,[hwnddlg],[lparam],MAX_GRID
Кроме того, процедуру можно использовать и для примагничивания окна при наступлении каких-либо условий, без необходимости перетаскивать его по экрану. Например:
  1. some_case:
  2.         ; Примагничивание окна при каком-либо событии
  3.         invoke  GetWindowRect,[hwnddlg],coord
  4.         stdcall StickyWindow,[hwnddlg],coord,MAX_GRID
  5.         mov     eax,[coord.right]
  6.         sub     eax,[coord.left]
  7.         mov     ebx,[coord.bottom]
  8.         sub     ebx,[coord.top]
  9.         invoke  MoveWindow,[hwnddlg],[coord.left],[coord.top],eax,ebx,TRUE
В приложении пример программы с исходным текстом, которая создает и обрабатывает магнитное окно, учитывая несколько мониторов. Оно притягивается к границам экрана при перемещении, а также при установке соответствующего чекбокса.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Advanced.Sticky.Window.Demo.zip (3,347 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 2014 | Комментариев: 0

Метки: Assembler, окна
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

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

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0084 сек.) / Память: 4.5 Mb
Наверх