Autohotkey
February 13, 2023

Панорамирование/скроллинг/рука в Office

🌰 Прокрутка офисных приложений нажатым колесом мыши, как принято в CAD

Введение

При работе в дизайнерских программах, при просмотре карт или PDF-документов Вы можете передвигать чертёж/карту/документ при зажатой кнопке мыши. В CADах это обычно средняя кнопка, в PDF — левая, в 2gis — левая или правая.

Обычно за включение этого режима отвечает кнопка с иконкой руки (panning hand), вроде такой:

Иконка руки

Однако в офисных приложениях нажатие на колесо вызывает режим прокрутки, когда документ начинает ползти в сторону перемещения мыши, что не всегда удобно. Попробуем вернуть «нормальное» поведение колеса в приложениях Microsoft Office.

Word

С word-ом проще всего. Функция «рука» встроена в ворд, она называется «Режим панорамирования»:

В этом режиме удобно редактировать графические элементы в word при большом увеличении. Если функция панорамирования в ворде нужна Вам периодически, то можно вынести её на панель быстрого доступа. А чтобы назначить эту функцию на среднюю кнопку мыши, используем следующий скрипт AHK:

#HotIf WinActive("ahk_class OpusApp") ; Когда открыт Word...
MButton:: WordPan()

WordPan(*) {
	if !(wd := GetWord()) ; Если ворд не берётся,
		return            ; то выход
	try {
		WD.ActiveWindow.View.Panning := True  ; рука вкл
		Click "down"                          ; зажали ЛКМ
		KeyWait "MButton"                     ; ждём отпускания колеса
		Click "up"                            ; отжали лкм
		WD.ActiveWindow.View.Panning := False ; рука выкл
	}
}

GetWord(Force:=0) {
	static wd := ""
	return GetComApp(&wd, force, "Word.Application", "Word")
}
Скачать все скрипты из статьи

В скрипте используется функция GetComApp.

Visio

В visio тоже несложно использовать «руку», но она активируется странным сочетанием Ctrl+Shift+Правая кнопка мыши. Поменяем на среднюю кнопку:

#HotIf WinActive("ahk_class VISIOA")

MButton:: {
	SendInput "{Ctrl down}{Shift down}"
	Sleep 20
	Send "{Rbutton down}"
	KeyWait "MButton"
	Send "{Rbutton up}"
	SendInput "{Ctrl up}{Shift up}"
}

Для большего сходства с автокадом включите опцию «Панорамирование с помощью IntelliMouse» в настройках visio, чтобы поворот колеса менял увеличение:

Данный флажок активирует увеличение/уменьшение масштаба путём вращения колеса (без ctrl)

Excel

А теперь начинается самое интересное. В экселе режима руки нет, хотя он был бы очень кстати для прокрутки широких таблиц влево-вправо. С другой стороны, в Excel имеется поддержка клавиши ScrollLock. Когда Scroll Lock активен (индикатор на клавиатуре горит), клавиши управления курсором ↑↓←→ не передвигают выделение, а осуществляют прокрутку листа, при этом выделение не сбивается. Попробуйте сами. Excel — одна из немногих программ, где работает Scroll Lock.

А ведь это решение! При зажатии колеса мыши мы будем активировать режим Scroll Lock, а при отжатии — снимать его. Останется периодически опрашивать положение мыши, и, если она передвинулась, сдвигать лист в соответствующую сторону клавишами ↑↓←→. Скрипт для Excel:

#HotIf WinActive("ahk_class XLMAIN") ; Excel
MButton:: ScrollLockPan ; Панорамирование СкроллЛоком

ScrollLockPan() {
	static XT := 30 ; Порог обнаружения движения мыши (пикселей)
	static YT := 30
	static MPOLLING := 50   ; Частота опроса мыши, мс
	mx0:=my0:=mx1:=my1 := 0 ; Начальные и конечные координаты мыши
	MouseGetPos &mx0, &my0
	SetScrollLockState 1    ; Скролл лок ВКЛ
	Loop {
		Sleep MPOLLING
		if !GetKeyState("MButton", "P") { ; Если колесо отпущено,
			SetScrollLockState 0          ; отключаем скролл лок
			return                        ; и выходим
		}
		MouseGetPos &mx1, &my1
		dx := mx1-mx0
		dy := my1-my0
		dx := round(dx/XT*1) ; Здесь значения можно подобрать по вкусу
		dy := round(dy/YT*4) ; Я умножил на 4, чтобы по вертикали двигалось поживее
		;tooltip "dx " dx " dy " dy
		if dX > 0 {
    		Send "{Left " dX "}" ; Нажимаем «влево» нужное число раз (в dX — число)
    	} else if dX < 0 {
    		Send "{Right " abs(dX) "}"
    	}
    	if dY > 0 {
    	    Send "{Up " dY "}"
    	} else if dY < 0 {
            Send "{Down " abs(dY) "}"
    	}
		mx0 := mx1
		my0 := my1
	}
}
Скачать все скрипты из статьи

Разумеется, перемещение листа будет не плавным, как в Word или Visio, а скачками: целыми колонками и строчками сразу. Также сдвиг не будет численно соответствовать количеству пикселей, на которые переместилась мышь. Да это и невозможно, ведь в общем случае все колонки имеют разную ширину, а строки отличаются по высоте.
Отрегулируйте параметры задержки (MPOLLING), порогов (XT, YT) и множители в формулах определения dX/dY так, как Вам будет удобнее.

PowerPoint

В PowerPoint тоже нет функции «рука». Действуем аналогично: опрашиваем положение мыши, вычисляем сдвиг, но вместо ScrollLock вызываем функцию SmallScroll, передавая в неё величину сдвигов:

#HotIf WinActive("ahk_class PPTFrameClass") ; PowerPoint 2010+
MButton:: ComPanP(GetPoint())

#HotIf WinActive("ahk_class PP12FrameClass") ; PowerPoint 2007
MButton:: ComPanP(GetPoint())

ComPanP(App) { ; Панорамирование для PowerPoint
	static XT := 20 ; Это для панорамирования колёсиком
	static YT := 20
	static MPOLLING := 50
	mx0:=mx1:=my0:=my1:=0
	MouseGetPos &mx0, &my0
	if !GetKeyState("MButton", "P")
		return
	loop {
		sleep MPOLLING
		MouseGetPos &mx1, &my1
		dx := mx1-mx0
		dy := my1-my0
		if !GetKeyState("MButton", "P")
			return
		dx := round(dx/XT*2)
		dy := round(dy/YT*4)
		;tooltip "dx " dx " dy " dy
		if (dX||dY) {
			try {
				App.ActiveWindow.SmallScroll -dY, dY, -dX, dX  ; Отрицательные значения не воспринимает
			} catch {
				Tooltip "Отпустите кнопку и начните ещё раз!"
				KeyWait "MButton"
				Sleep 50
				SendLevel 1
				SendEvent "^+{MButton}"
				SendLevel 0
				Tooltip
				return
			}
			mx0 := mx1
			my0 := my1
		}
	}
}

GetPoint(Force:=0) {
	static pp := ""
	return GetComApp(&pp, force, "PowerPoint.Application", "PowerPoint", "PP_")
}
Скачать все скрипты из статьи

Аналогично, параметры настраиваем по вкусу.

В powerpoint есть неприятная особенность: слайд переключатся на следующий, если прокрутить в самый низ или верх слайда. См. Как отключить переход к следующему слайду колёсиком в PowerPoint⁠⁠.

Другие программы

Использованный приём с регулярным опросом мыши можно распространить и на другие приложения, где Вам хотелось бы добавить панорамирование. Вопрос в том, как сообщать программе, что мы хотим сдвинуть её окно. Один из способов сделать это — послать окну программы сообщение о прокрутке:

;           0x114 — горизонтальная прокрутка
;           0x115 — вертикальная
;             |   1/0 — направление 
;             |    |     id элемента управления
;             |    |      |    HWND окна
;             |    |      |     |
;             ↓    ↓      ↓     ↓
PostMessage 0x114, 1, 0, ctrl, win

Узнать id элемента управления можно задав ClassNN, полученный с помощью утилиты Window spy из комплекта программы AutoHotKey:

Вот пример скрипта для WordPad:

#HotIf WinActive("ahk_class WordPadClass") ; WordPad

MButton::PanWordPad()

PanWordPad(*) {
	static XT := 30
	static YT := 30
	static MOUSEPOLLING := 50
	mx0:=my0:=mx1:=my1:=0
	
	win := WinGetID("A") ; получим id активного окна
	ctrl := ControlGetHWND("RICHEDIT50W1", win) ; получим id основного элемента управления, который двигать будем
	
	MouseGetPos &mx0, &my0
	Loop {
		Sleep MOUSEPOLLING
		if !GetKeyState("MButton", "P") {
			return
		}
		MouseGetPos &mx1, &my1
		dx := mx1-mx0
		dy := my1-my0
		dx := -round(dx/XT*3)
		dy := -round(dy/YT*1)
		;tooltip "dx " dx " dy " dy
        if (dX||dY) {
        	if dX > 0 {
        		loop dX 
        			PostMessage 0x114, 1, 0, ctrl, win ; вправо
        	} else if dX < 0 {
        		loop abs(dX)
        			PostMessage 0x114, 0, 0, ctrl, win ; влево
        	}
        	if dY > 0 {
        		loop dY
        			PostMessage 0x115, 1, 0, ctrl, win ; вниз
        	} else if dY < 0 {
        		loop abs(dY)
        			PostMessage 0x115, 0, 0, ctrl, win ; вверх
        	}
        }
		mx0 := mx1
		my0 := my1
	}
}

Файл со всем кодом из статьи