How to Handle User Interface Events

Part of: How-To
Revised: 1-Feb-2010
Original: 1-June-2001

Описывает, как события обрабатываются в системе REBOL View.

Return to: REBOL How To

Contents

Как запустить примеры
Объект Feel и его функции
Feel функции
Перересовка Feel
Пример функций перерисовки
The Over Feel
Примеры функций Over
Что-то делать с Over
Перекрывающиеся грани
Непрерывный по событиям
Чувство вовлеченности
Пример события мыши
Пример перетаскивания
События клавиатуры
События таймера
Обноружение Feel
Пример обнаружения
Window Level Detect

Как запустить примеры

Чтобы запустить любой из примеров, показанных ниже, запустите текстовый редактор и создайте строку заголовка REBOL, например:

REBOL [Title: "Example"]

Затем просто вырежьте пример текста и вставьте его после этой строки. Сохраните текстовый файл и дайте ему имя, например example.r. Затем запустите файл так же, как и любой файл REBOL.

Объект Feel и его функции

Каждый графический объект, отображаемый REBOL, - это ЛИЦО. Внешний вид лица определяется полями объекта. Одно поле объекта определяет ОЩУЩЕНИЕ объекта и определяет, как объект ведет себя при вводе пользователем данных и событиях. Когда вы нажимаете клавишу на клавиатуре или перемещаете и щелкаете мышью, именно объект FEEL определяет, как обрабатывается действие.

Система событий REBOL/View довольно элегантна и развивалась за много лет до выпуска. Таким образом, Visual Interface Dialect (VID) может создавать различные варианты поведения для десятков объектов пользовательского интерфейса всего в нескольких сотнях строк кода.

Feel функции

Вся система пользовательского интерфейса REBOL обрабатывается этими четыре функции:

redraw [face action position]

over [face action position]

engage [face action event]

detect [face event]

Каждая из этих функций имеет определенное назначение в системе событий пользовательского интерфейса:

redrawвызывается непосредственно перед отрисовкой лица, что позволяет изменять определенные атрибуты лица перед его отображением. Эта функция вызывается каждый раз, когда лицо обновляет свой вид, а также каждый раз, когда оно отображается или скрывается.
overвызывается всякий раз, когда указатель мыши проходит над лицом или от лица. Это может происходить с очень высокой скоростью, потому что пользовательский интерфейс может состоять из сотен лиц, и пользователь может много перемещать указатель мыши по этим лицам. По этой причине это единственная функция, которая не сочетается с функцией включения. Для этой функции следует установить значение NONE, если в этом нет необходимости, позволяя системе игнорировать лицо при наведении курсора мыши на него.
engageвызывается всякий раз, когда для лица происходит событие. Эта функция обрабатывает такие события, как нажатие мыши, повышение, альтернативное нажатие, двойной щелчок, таймеры, клавиши клавиатуры и многое другое.
detectвызывается всякий раз, когда происходит какое-либо событие для лица или для любых лиц, содержащихся в нем. Это позволяет лицу перехватывать события, нацеленные на графические объекты более низкого уровня.

ЛЛюбое лицо может иметь объект ЧУВСТВИТЕЛЬНОСТЬ с одной или несколькими из вышеперечисленных функций. Это позволяет обрабатывать события от любого типа графического объекта, включая изображения, текст, поля или линии.

Обратите внимание, что большинство объектов системы имеют общие объекты чувств. Например, лица BUTTON используют один объект FEEL, который определяет действия кнопки. Изменение объекта FEEL кнопки изменит объект FEEL всех других кнопок. Иногда этот эффект может быть нежелательным. Чтобы избежать этого эффекта, вы можете клонировать объект FEEL и изменять его только для того лица, которое вам нужно.

Перересовка Feel

Функция REDRAW feel весьма полезна, когда вам нужно изменить внешний вид лица непосредственно перед его отрисовкой. Это может свести к минимуму объем кода, необходимого для создания эффектов состояния лица, таких как выделение или изменение изображения при щелчке мышью по лицу.

Функция REDRAW имеет вид:

redraw face action position

Аргументы функции:

faceПерерисовываемый объект лица.
actionСлово, обозначающее действие, которое произошло на лице: DRAW, SHOW, HIDE (НАРИСОВАТЬ, ПОКАЗАТЬ, СКРЫТЬ.)
positionПоложение XY лица, если оно повторяется. Повторяющиеся грани будут отражены в другом документе. Пока вы можете проигнорировать этот аргумент.

Пример функции перерисовки

Вот очень простой пример, который поможет вам понять функцию REDRAW:

view layout [
    the-box: box "A Box" forest feel [
        redraw: func [face act pos] [print act]
    ]
    button "Show" [show the-box]
    button "Hide" [hide the-box]
]

Эта функция будет печатать слово действия, которое передается при каждом вызове функции REDRAW. Когда вы запустите пример, вы увидите, что консоль сразу напечатает:

show
draw

Показ происходит, когда функция VIEW запрашивает отображение окна. Затем отрисовка происходит непосредственно перед отрисовкой лица.

Если вы нажмете кнопку «Показать», вы увидите, что выполняются те же два действия:

show
draw

На этот раз show делается прямо на лице.

При нажатии кнопки «Hide» вывод будет:

hide

Нет show, значит, нет и draw either.

The Over Feel

Функция OVER feel определяет, как мышь проходит по лицу. Его можно использовать для визуальной обратной связи, что указатель мыши находится над объектом, или он может отображать справочную информацию об элементе, когда пользователь наводит на него указатель мыши.

Функция OVER имеет вид:

over face action position

Аргументы для функций OVER:

faceОбъект лица под указателем мыши.
actionЛогическое значение, которое имеет значение TRUE (ИСТИНА) или FALSE (ЛОЖЬ), чтобы указать, входит ли мышь в лицо или выходит из него.
positionТекущее положение X-Y мыши.

Примеры функции Over

Вот простой пример, который поможет вам понять функцию OVER:

print "Displaying..."

view layout [
    box "A Box" forest feel [
        over: func [face act pos] [print [act pos]]
    ]
]

Запустите этот пример и наведите указатель мыши на поле. Сначала откроется консоль, потом окно с коробкой. Убедитесь, что окно с блоком является активным окном, и наведите указатель мыши на блок.

Когда мышь войдет в поле, вы увидите дисплей консоли:

true 23x72

И когда указатель мыши выйдет из окна, вы увидите такое сообщение:

false 120x73

Аргумент действия - ИСТИНА, когда мышь входит, и ЛОЖЬ. когда мышь выходит.

Обратите внимание, что первый PRINT в этом примере выполняется, чтобы открыть окно консоли и отобразить результаты OVER. Если этого не сделать, то при первом наведении курсора мыши на поле откроется консоль. В некоторых системах, таких как Windows, окно, содержащее поле, теряет фокус, и функция OVER немедленно возвращает FALSE. Вы можете увидеть это, если удалите первую строку PRINT.

Делаем чтото с Over

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

view layout [
    box "A Box" forest feel [
        over: func [face act pos] [
            face/text: either act ["Over"]["Away"]
            show face
        ]
    ]
]

Он показывает поле со словом «Over», когда указатель мыши находится над прямоугольником, или «Away», когда мышь находится вне поля.

Обратите внимание, что функция SHOW была вызвана для отображения нового текста для лица.

Вот пример, который меняет цвет лица при наведении курсора мыши:

view layout [
    box "A Box" forest feel [
        over: func [face act pos] [
            face/color: either act [brick][forest]
            show face
        ]
    ]
]

Вот пример, показывающий "справочную строку" в зависимости от того, где находится мышь:

view layout [
    box "Top Box" forest feel [
        over: func [face act pos] [
            helper/text: either act ["Over top box."][""]
            show helper
        ]
    ]
    box "Bottom Box" navy feel [
        over: func [face act pos] [
            helper/text: either act ["Over bottom box."][""]
            show helper
        ]
    ]
    helper: text 100
]

Перекрывающиеся грани

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

print "Displaying..."

view layout [
    box "A Box" forest feel [
        over: func [face act pos] [print ["A Box:" act]]
    ]
    pad 30x-40
    box "B Box" brick feel [
        over: func [face act pos] [print ["B Box:" act]]
    ]
]

Когда мышь переходит в поле A, консоль печатает:

A Box: true

Когда мышь перемещается из поля A в поле B, консоль печатает:

A Box: false
B Box: true

Сначала он указывает, что указатель мыши больше не находится над полем A, а затем сообщает вам, что он находится над полем B.

Непрерывный по событиям

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

print "Displaying..."

out: layout [
    box "A Box" forest feel [
        over: func [face act pos] [print [act pos]]
    ]
]

view/options out [all-over]

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

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

The Engage Feel

Функция включения вызывается всякий раз, когда для лица происходит какое-либо событие, кроме REDRAW или OVER. Он обрабатывает события щелчка мыши, ввод с клавиатуры, таймеры и другие типы событий.

TФункция ENGAGE имеет вид:

engage face action event

Где его аргументы:

faceЛицо с событием.
actionСлово, обозначающее произошедшее действие.
eventСобытие, предоставляющее подробную информацию о действии.

Пример события мыши

Вот небольшой пример, который напечатает события мыши, происходящие в поле:

view layout [
    box "A Box" forest feel [
        engage: func [face action event] [
            print action
        ]
    ]
]

Запустите этот пример и щелкните поле. Щелкните правой кнопкой мыши на коробке. Нажмите кнопку мыши вниз, затем переместите мышь, как если бы вы перетаскивали прямоугольник.

При использовании мыши вы увидите поток таких событий, как:

down
up
alt-down
alt-up
down
over
over
over
up
down
over
over
over
away
away
away
away
away
up

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

downнажата основная кнопка мыши.
upбыла отпущена основная кнопка мыши.
alt-downбыла нажата альтернативная кнопка мыши (правая кнопка).
alt-upальтернативная кнопка мыши была отпущена.
overмышь перемещалась по лицу при нажатии любой кнопки.
awayмышь отлетела от лица, пока была нажата кнопка.

Пример перетаскивания

Используя некоторые из вышеперечисленных действий, вот пример, который показывает, как перетаскивать лицо внутри окна:

view layout [
    size 200x200
    box 40x40 coal "Drag Box" font-size 11 feel [
        engage: func [face action event] [
            if action = 'down [start: event/offset]
            if find [over away] action [
                face/offset: face/offset + event/offset - start
                show face
            ]
        ]
    ]
]

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

Если начальная позиция хранится внутри грани блока, то можно создать стиль и создать несколько блоков перетаскивания из одного стиля:

view layout [
    size 240x240
    style dragbox box 40x40 font-size 11 feel [
        engage: func [face action event] [
            if action = 'down [face/data: event/offset]
            if find [over away] action [
                face/offset: face/offset + event/offset - face/data
                show face
            ]
        ]
    ]
    dragbox "Box 1" navy
    dragbox "Box 2" teal
    dragbox "Box 3" maroon
    dragbox "Box 4" gold
]

Теперь вы можете перетащить любой из четырех прямоугольников. Обратите внимание, что вы можете перетаскивать прямоугольники под другие поля. Стиль DRAGBOX определяется новой функцией ENGAGE, которая сохраняет начальную позицию в поле данных лица.

Чтобы вывести текущую грань наверх, переместите ее в конец списка лицевых панелей окна при возникновении события «вниз»:

view layout [
    size 240x240
    style dragbox box 40x40 font-size 11 feel [
        engage: func [face action event] [
            if action = 'down [
                face/data: event/offset
                remove find face/parent-face/pane face
                append face/parent-face/pane face
            ]
            if find [over away] action [
                face/offset: face/offset + event/offset - face/data
            ]
            show face
        ]
    ]
    dragbox "Box 1" navy
    dragbox "Box 2" teal
    dragbox "Box 3" maroon
    dragbox "Box 4" gold
]

В примере используется поле parent-face для доступа к списку панелей окна. Вы также можете назначить макет окна переменной и использовать ее для ссылки на список панелей.

События клавиатуры

Чтобы получать события клавиатуры, лицо должно быть в фокусе, прежде чем они будут отправлены. Вот пример:

view/new layout [
    the-box: box "A Box" forest feel [
        engage: func [face action event] [
            print [action event/key]
        ]
    ]
]
focus the-box
do-events

Когда вы печатаете на клавиатуре, вы увидите такой поток, как:

key a
key b
key c
key d

event/key содержит символ кода клавиши для нажатой клавиши. was pressed.

Если вы нажмете функциональные клавиши, вы увидите:

key home
key end
key up
key down
key f1
key f5

Однако это не коды клавиш; это слова. В REBOL нет необходимости декодировать последовательности клавиатуры. Они расшифрованы для вас. Это упрощает написание обработчика ключей. Вот пример:

view/new layout [
    the-box: box "A Box" forest feel [
        engage: func [face action event] [
            if action = 'key [
                either word? event/key [
                    print ["Special key:" event/key]
                ][
                    print ["Normal key:" mold event/key]
                ]
            ]
        ]
    ]
]
focus the-box
do-events

Чтобы определить, удерживаются ли клавиши Control или Shift, вы можете написать условные тесты, такие как:

if event/control [...]

if event/shift [...]

if event/control/shift [...]

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

scroll-line

и если управляющая клавиша удерживается:

scroll-page

Величину прокрутки можно определить по полю смещения Y события. Добавьте это к одному из приведенных выше примеров.

if action = 'scroll-line [print event/offset/y]

Размер смещения Y определяется чувствительностью колеса прокрутки, установленной для вашей операционной системы.

События таймера

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

view layout [
    box "A Box" forest rate 1 feel [
        engage: func [face action event] [
            print action
        ]
    ]
]

Скорость может указывать либо количество событий в секунду, либо период между событиями. Если вы использовали:

rate 10

тогда вы получите десять событий в секунду. Если вы написали:

rate 0:00:10

тогда вы будете получать событие времени каждые десять секунд. Или же,

rate 0:10

будет отправлять вам временное событие каждые 10 минут.

Вот цифровые часы, основанные на временных событиях:

view layout [
    origin 0
    banner "00:00:00" rate 1 feel [
        engage: func [face act evt] [
            face/text: now/time
            show face
        ]
    ]
]

Чтобы создать единичное временное событие, которое происходит только один раз, вы можете отключить таймер внутри события:

ticks: 0

view layout [
    box "A Box" forest rate 0:00:10 feel [
        engage: func [face action event] [
            if action = 'time [
                if ticks > 0 [
                    face/rate: none
                    show face
                ]
                ticks: ticks + 1
                print now
            ]
        ]
    ]
]

В первый раз событие происходит сразу. (Ошибка во временных событиях.) Переменная TICKS хранит счетчик того, сколько раз произошло событие. Чтобы выключить таймер, устанавливается значение NONE, и на лицевой стороне вызывается SHOW для обновления внутренних значений таймера.

Обнаружение Feel

Функция DETECT похожа на ENGAGE, но имеет возможность перехватывать события для всех своих подпрограмм. DETECT может использоваться для обработки особых событий, таких как ввод с клавиатуры, таймеры или события мыши.

Функция DETECT работает как фильтр событий. Когда происходит событие, DETECT может решить, как его обработать. Когда это будет сделано, функция может разрешить событию продолжить снижение уровня граней или немедленно остановить его.

DETECT не следует использовать, если ENGAGE может иметь дело с событием напрямую. ОБНАРУЖЕНИЕ требуется только тогда, когда необходимо отфильтровать события, направленные на подпространства.

Функция DETECT имеет вид:

detect face event

Где его аргументы:

faceЛицо с событием.
eventСобытие, дающее подробную информацию.

Функция DETECT должна возвращать:

eventТо же событие, которое было передано в качестве аргумента.
none Когда событие не должно обрабатываться субфайлами.

Пример обнаружения

Вот пример, который напечатает каждое событие, полученное лицевой стороной коробки:

print "Running.."

view layout [
    box 200x200 "A Box" navy feel [
        detect: func [face event] [
            print event/type
            event
        ]
    ]
]

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

lock-out: off

out: layout [
    the-box: box 240x140 teal feel [
        detect: func [face event] [
            if event/type = 'alt-down [
                lock-out: off
                vt/text: "Back to normal."
                show vt
            ]
            if not lock-out [return event]
            return none
        ]
    ]
]

out2: layout [
    space 0x8
    field "Type here"
    across
    button "Lock Out" [
        lock-out: on
        vt/text: trim/lines {Events are locked out.
            Right click to resume.}
        show vt
    ]
    button "Quit" [quit]
    return
    vt: vtext bold 200x30
]

the-box/pane: out2/pane

view out

Здесь функция обнаружения возвращает событие, только если для переменной LOCK-OUT установлено значение false. В противном случае он возвращает NONE, и никакие события не передаются в подчиненные уровни.

Обнаружение уровня окна

TЧтобы использовать функцию DETECT на уровне окна, вы должны установить функцию DETECT feel после того, как произойдет просмотр окна VIEW, в противном случае функция VIEW переопределит ваше FEEL. Вот пример:

out: layout [banner "Testing"]

view/new out

out/feel: make out/feel [
    detect: func [face event] [...]
]

Для установки обработчиков событий окна лучше использовать функцию INSERT-EVENT-FUNC. Эта функция позволяет использовать несколько обработчиков для каждого окна. Об этом пойдет речь отдельно.

About | Contact | PrivacyREBOL Technologies 2021