Опубликован: 10.12.2007 | Уровень: специалист | Доступ: платный
Лекция 6:

События

6.3. Что происходит при движениях мыши

К жестам мыши относятся нажатия на ее кнопки, перемещения мыши и прокрутка с помощью ролика. Mozilla поддерживает их все. Mozilla также поддерживает работу с графическими планшетами и другими устройствами, которые могут имитировать мышь.

Жесты мыши могут быть простыми и сложными. Как символы, полученные по нажатию клавиш, можно составить в слово для поиска, так и движения мыши и нажатия на ее кнопки можно сложить вместе в одно большее действие с собственным особым значением. Такое действие и есть настоящий жест мышью. Простое нажатие кнопки мыши - тривиальный жест. Примеры нетривиальных жестов включают в себя перетаскивание и выделение мышью.

Тривиальные жесты мышью обрабатываются в Mozilla соответственно DOM 3 Events. Эти события - простейшие (атомарные) операции, из которых составляются более сложные жесты; они перечислены в таблице 6.2. Пользуйтесь ими, как и любыми другими DOM-событиями. Поддержка более сложных жестов рассматривается в дальнейших разделах.

Почему поддержка сложных жестов может оказаться необходимой? Пользователи хорошо знают несколько основных движений мышью и ожидают поддержки этих движений некоторыми приложениями. Самый распространенный пример - программа обработки изображений или рисования, в таких программах есть возможность простого рисования и изменения размеров и форм фигур перетаскиванием границ. Новые и экспериментальные применения жестов следует реализовывать аккуратно, так как пользователь может запутаться.

В большинстве случаев сложные жесты мышью должны быть реализованы в JavaScript. Хотя Mozilla может перехватывать и обрабатывать поток событий mousemove в JavaScript удивительно быстро, такая обработка расходует большое количество ресурсов процессора. Плохо реализованная (или даже хорошо реализованная) поддержка жестов мышью в JavaScript может быть слишком требовательна к процессору в случае старых компьютеров.

6.3.1. Выделение

Выделение - это жест мышью, при котором выбирается часть документа от начальной точки до конечной. При этом обычно присутствует визуальная реакция на данную операцию: чтобы обозначить расширение текущего выделения, часть текста затемняется или подсвечивается. Выделения могут быть совсем маленькими, состоящими из единственного элемента меню или списка или из нескольких символов. Mozilla поддерживает несколько типов выделений.

Простейший из них - выделение целых кусков данных. Это относится к XUL-элементам управления вроде меню, списков и деревьев, а также их HTML-эквивалентам, если таковые существуют. Откройте меню - при передвижении мыши поверх него, выделяется один элемент за раз. Такой тип выделения кажется частью обычного поведения элемента управления, а начальная и конечная точки пользователем обычно не задаются. Выбор пункта меню - это что-то, над чем мы обычно не задумываемся.

Более показательно выделение в полуструктурированном документе, когда пользователь мышью определяет начальную и конечную точки выделения. Этот тип выделения чаще всего встречается в текстовых процессорах и редакторах. В таких системах выделенные данные отделяются от жеста мышью, с помощью которого выделение было создано. Типичный жест для выделения выглядит примерно так:

  1. нажатие левой кнопки мыши в начальной точке выделения;
  2. перемещение мыши к конечной точке выделения, удерживая кнопку мыши нажатой;
  3. отпускание левой кнопки мыши в конечной точке выделения.

Однако это правило выполняется не всегда. Совсем другой жест с тем же эффектом может быть таким:

  1. нажатие и отпускание левой кнопки мыши в начальной точке выделения;
  2. перемещение мыши к конечной точке выделения;
  3. перемещение мыши к конечной точке выделения;

Таких вариаций может быть много. Например, нажатие кнопки мыши с удерживаемым Shift также часто используется для расширения выделенной части до текущей точки. В Mozilla с помощью одного жеста - нажать- переместить-отпустить - реализуются два типа выделения. Первый - выделение в текстовых полях. Второй - выделение любых данных в отображаемом документе.

Выделение текста работает внутри HTML-тегов <input type="text"> и <textarea> и XUL-тега <textbox>. Элемент DOM 1 для этих тегов содержит свойства и методы для просмотра и задания выделения:

  • value - все содержимое текстового поля;
  • selectionStart - позиция символа, являющегося начальной точкой выделения;
  • selectionEnd - позиция символа, являющегося конечной точкой выделения;
  • setSelectionRange(start,end) - задает начальную и конечную точки выделения.

Сюда относится также свойство user-select из черновика CSS 3.

Выделение данных вообще (за исключением элементов управления) реализовано только для HTML. Отправной точкой в изучении выделений в HTML может послужить метод document.getSelection(), который возвращает объект, содержащий массив из объектов Range. Объекты Range определены в стандарте DOM 2 Traversal and Ranges. По умолчанию пользователь не может выделить мышью XUL-данные. Однако эту функциональность можно добавить. Начать можно с метода createRange(), относящегося к объекту XUL-документа. Этот метод создает DOM-объект Range, который можно изменить так, чтобы он соответствовал любой непрерывной части дерева XUL-документа. При перемещении мыши этот объект можно динамически обновлять, а к поддереву, представляющему выделение, можно также динамически применять правила стилей для подсветки выделяемых данных.

И в HTML, и в XUL этот тип выделения ограничивается выделением целых, непрерывных строк данных (другими словами, блоков строк, см. "Верстка с XUL" , "Проектирование с XUL"), за исключением первой и последних строк, которые могут быть выделены частично. Исключение из этого правила - вертикально отображаемый текст, как в иврите или китайском, а также столбцы HTML-таблиц (см. "Множественное выделение").

Если вы решитесь поэкспериментировать с DOM-объектом Range, помните, что хотя он и полностью реализован, в Mozilla он используется не так долго и тщательного тестирования не проходил.

6.3.1.1. Выделение прямоугольником

Выделение прямоугольником более всего известно по окнам графических сред, в которых используются пиктограммы (или значки, или иконки). Если, щелкнув по фону окна или рабочего стола и удерживая кнопку нажатой, перемещать указатель мыши, появится прямоугольник с пунктирными границами. Все пиктограммы, попадающие в него, выделяются. Это один из видов выделений данных, но выделяемая часть необязательно должна представлять собой целые идущие друг за другом строки. В данной прямоугольной области могут быть выделены любые данные.

В Mozilla поддержка такого выделения отсутствует вообще, но платформа предоставляет достаточно возможностей для ее реализации. Это выделение может поддерживаться и в HTML, и в XUL. Здесь мы обсудим XUL-вариант.

Так как в XUL не поддерживаются слои и CSS-стили z-index, создать сам прямоугольник для выделения проблематично. Решение - убедиться, что все содержимое XUL-документа находится в одной карте тега <stack> (см. "Верстка с XUL" , "Проектирование с XUL"). Прямоугольник для выделения - это тег <box> без содержимого и границами с определенным стилем. Он один находится в своей карте стека. В обычных условиях у этой второй карты есть стиль visibility:none.

Когда пользователь начинает совершать жест мышью, скрипт делает прямоугольник выделения видимым. При перемещении указателя мыши свойства ширины и высоты прямоугольника меняются соответствующим образом. При каждом обновлении mousemove контрольная процедура проверяет все элементы DOM-дерева и меняет стили у тех из них, что находятся внутри текущего прямоугольника. Все просто.

6.3.1.2. Множественное выделение

Множественное выделение более всего знакомо благодаря окнам рабочего стола с пиктограммами. В Microsoft Windows или, например, GNOME несколько пиктограмм могут быть выделены щелчком левой кнопкой мыши с удерживанием Control по каждой из них. Этот способ отличается от выделения прямоугольником, когда вся область выделяется за раз.

Mozilla поддерживает множественное выделение в тегах <listbox> и <tree>. Описание работы этих тегов можно найти в "Списки и Деревья" , "Списки и деревья". Для множественного выделения здесь используется щелчок с удерживанием Control.

Mozilla также поддерживает множественное выделение в HTML для вертикально ориентированного текста. Поддержка такого текста, который называется BiDi-текстом (bi-directional, двунаправленный) в XUL еще отсутствует. Жест для этого вида выделения такой же, как и для обычного выделения данных, но обработка происходит по-другому. При использовании фабричного метода getSelection() для объекта HTML- документа вместо одного объекта Range создается их массив. Каждый из этих объектов отражает одну вертикальную полосу (один столбец выделенного содержимого). Метод поиска по индексу getRangeAt() возвращает эти объекты.

Привычное выделение по щелчку мыши с удерживаемым Control в Mozilla реализовать легко. События keydown и keyup при нажатии, а затем отпускании Control можно использовать для определения начала и конца жеста. События щелчка мышью можно использовать для определения выделенных объектов во время жеста. Завершение одного щелчка совсем не значит, что жест закончился. Жест заканчивается тогда, когда программист говорит, что он закончился.

6.3.2. Перетаскивание

Перетаскивание - это жест, в котором визуальный объект выбирается и перемещается вместе с указателем мыши, пока он не будет отпущен. Дополнительной особенностью этого жеста может быть использование целевых областей. Целевая область - это такая часть окна, в которой операция перетаскивания может завершиться успешно. Если эти области существуют, при "пронесении" над ними объекта они должны подсвечиваться. Классический пример - пиктограмма мусорной корзины в Macintosh. При помещении пиктограммы документа поверх нее эта корзина темнеет. Если используются целевые области, обычно перетаскиваемый объект после успешного перемещения исчезает из вида.

Перетаскивание может осуществляться внутри окна приложения, между окнами приложения или между окнами разных приложений. Поддержка перетаскивания в Mozilla спроектирована для жестов внутри одного окна Mozilla. Теоретически эту функциональность можно расширить для жестов между окнами Mozilla, но поддержка перетаскивания объектов из других приложений или графической среды и в них минимальна. Можно определять, когда при перетаскивании с рабочего стола (или на него) объект попадает в окно Mozilla или покидает его, и собирать или отправлять перетаскиваемые данные.

Основное ограничение жестов перетаскивания в Mozilla - то, что перемещаемый объект не следует за указателем мыши. Во время перетаскивания Mozilla позволяет только менять указатели мыши и иногда информацию о стилях, которые подсказывают, что перетаскиваемый объект находится над целевой областью. Это ограничение можно обойти, используя стек так, как описано в разделе "Выделение прямоугольником". Вместо прямоугольника выделения во второй карте стека находится копия перемещаемого объекта. Этот объект можно анимировать так, чтобы он следовал за указателем мыши, используя методики динамического HTML.

Поддержка перетаскивания в Mozilla - это мозаика из нескольких кусочков.

Первый кусочек мозаики - события и их обработчики. Для простого перетаскивания необходимы три события: draggesture (начало перетаскивания), dragover (эквивалент mouseover) и dragdrop (конец перетаскивания). Два дополнительных события используются в более сложных случаях, когда перемещаемый объект попадает в окно Mozilla извне или, наоборот, выходит за его пределы. Это события dragenter и dragexit.

Второй кусочек - XPCOM-объекты. Самая важная пара "компонент-интерфейс":

@mozilla.org/widget/dragservice;1 nsIDragService

Это та часть Mozilla, которая отвечает за жест в процессе перетаскивания.

Вызов метода invokeDragSession() интерфейса nsIDragService начинает сеанс перетаскивания. В особых случаях этот метод может принимать объект nsIScriptableRegion. Данный объект - набор прямоугольников в виде их координат в пикселах. Это нужно в тех случаях, когда область, над которой происходит перетаскивание, включает сложный элемент управления, состоящий полностью из графических элементов низкого уровня. Набор прямоугольников определяет активные участки (возможные целевые области) этого элемента управления, которые обрабатываются самим элементом, если указатель мыши попадает на любой из этих участков. Такая система позволяет пользователю получать реакцию от элемента управления, с которым нельзя работать из скриптов или если скрипты для него отсутствуют. Данная методика полезнее всего во встроенных системах и не используется в платформе Mozilla, если не задействуется тег <tree>. В большинстве случаев вместо объекта nsIScriptableRegion следует использовать null.

Метод getCurrentSession() возвращает объект nsIDragSession. Этот объект - простой набор состояний о текущем жесте перетаскивания и собственных методов не содержит. В каждом окне Mozilla за раз может происходить только одна операция перетаскивания объекта.

Третий кусочек мозаики - поддержка JavaScript. Хотя управление жестом перетаскивания и реализовано, результат жеста (подразумеваемая команда) должен быть написан программистом самостоятельно. В простом случае это означает использование интерфейсов nsIDragService и nsIDragSession в начале жеста, определение начальной и конечной точек жеста, предоставление стилей или другой реакции во время выполнения жеста, а также выполнение в конце команды, подразумеваемой жестом.

В более сложном случае жест может привести к импорту или экспорту данных с рабочего стола или на него, так что скрипт, обеспечивающий жест, должен также копировать данные из интерфейса nsITransferable или в него. Этот интерфейс работает с буфером обмена графической среды (буфер копирования и вставки).

В целом, написание скриптов для реализации перетаскивания - нетривиальная задача. Для упрощения процесса доступны два объекта JavaScript.

Файл chrome nsDragAndDrop.js из архива toolkit.jar реализует объект nsDragAndDrop, который устанавливается как свойство объекта окна. Этот объект предоставляет методы, делающие почти всю описанную выше черную работу, включая особую поддержку XUL-тега <tree>. Для этого файла нет XPIDL-определения - есть только то, что присутствует в самом .js-файле. Для каждого из событий перетаскивания существует соответствующий метод.

Второй объект должен создаваться автором приложения. Это обычный объект JavaScript, у которого также нет XPIDL-определения. Он должен создаваться соответствующими методами. Этот объект - набор обработчиков событий, по одному на каждое событие. Независимо от того, какому шаблону он может следовать, объект такого типа создается и присваивается переменной, обычно с именем вроде myAppDNDObserver.

Вместе эти два объекта сокращают размер каждого требуемого обработчика перетаскивания до одной строки скрипта. Сначала событие обрабатывается общим кодом nsDragAndDrop, а затем - любым специальным кодом, предоставляемым myAppDNDObserver. Чтобы посмотреть, как работает эта система, взгляните на простой пример в адресной книге Mozilla. Обратите внимание на использование abResultsPaneObserver (который мог бы называться abResPaneDNDObserver ) в messenger.jar в chrome.

В таблице 6.4 показаны соответствия между этими удобными методами и базовыми событиями.

Таблица 6.4. Соответствия между событиями перетаскивания и методами скриптов
Событие метод nsDragAndDrop метод -DNDObserver
Draggesture startDrag() onDragStart()
Dragover dragOver() onDragOver()
Dragdrop drop() onDrop()
dragexit dragExit() onDragExit()
dragenter dragEnter() onDragEnter()
6.3.3. Изменение размеров окна

Изменения размеров окон - следствие жеста мыши, реализованного в Mozilla на C/C++. См. описание тега <resizer> в "Первые элементы управления и темы" , "Первые элементы управления и темы".

6.3.4. Расширенная поддержка жестов

Жесты мышью могут быть менее формальными, чем описанные выше примеры. Как дирижер оркестра взмахивает палочкой, особенные движения и щелчки мышью могут заставить классический браузер выполнять команды меню вроде "Добавить в закладки", "Назад" или "Сохранить". Поддержка этих жестов реализована и для браузера Mozilla.

В простейшем случае такая поддержка жестов состоит из разделения движений мышью на несколько простых "взмахов". Эти более мелкие движения идентифицируются с использованием воображаемой сетки окна, каждая из клеток которой - определенной ширины и высоты. "Взмах" считается завершенным, когда события mousemove указывают на то, что указатель мыши покинул одну клетку и перешел в другую. Эти "взмахи" составляют набор простых инструкций, которые вместе позволяют однозначно определить использующийся жест. После получения полного набора инструкций жест определяется, и выполняется соответствующая ему команда.

Проект Optimoz предоставляет такую систему жестов, полностью реализованную на JavaScript. Ее можно найти на сайте http://www.mozdev.org. Код этого расширения небольшой и понятный. Как и в случае опережающего поиска, система жестов перехватывает события во время фазы возврата, избегая столкновения с другими потребителями тех же самых событий.

Дмитрий Гуменюк
Дмитрий Гуменюк
Россия, Звенигород
Konstantin Grishko
Konstantin Grishko
Россия, Москва, Московский финансово-промышленный университет "Синергия", Москва