BearLibTerminal / API / Ввод

===== ==== Функции ==== В BearLibTerminal ввод обрабатывается с помощью функций [[#read]], [[#peek]], [[#read str]], [[#has input]], [[#state]] и [[#check]]. === read === Эта функция возвращает следующее событие из [[#очередь ввода|очереди ввода]]: int key = terminal_read(); if (key == TK_ESCAPE) { // Quit } Если в очередь ввода пуста, вызов заблокирует выполнение до поступления нового события. Если такое блокируещее поведение нежелательно, можно проверить [[#has input]] перед вызовом **read**. === peek === Эта функция аналогична [[#read]], но не убирает прочитанное событие из внутренней очереди (следующий вызов peek или read вернет то же значение). Кроме того, вызов этой функции является неблокирующим: если очередь ввода пуста, функция вернет 0. === read_str === Эта функция выполняет простое блокирующее чтение строки без какого-либо разбора введенного значения. В процессе выполнения вызова, пользовательский ввод отображается по указанным координатам и ограничивается по размеру и может быть легко вписан в интерфейс игры. Функция отображает ввод в текущем выбранном слое и полностью восстанавливает исходное состояние сцены перед выходом, оставляя сохранение введенной строки на экране на усмотрение приложения. Функция возвращает размер строки, введенной пользователем и подтвержденной нажатием на Enter, или константу TK_INPUT_CANCELLED, если ввод был отменен нажатием на Escape или вовсе попыткой закрытия окна. wchar_t s[32] = L"Hello"; if (terminal_read_wstr(2, 1, s, sizeof(s)-1) >= 0) { terminal_wprintf(L"You entered: \"%ls\"", s); } Если поведение **read_str** не удовлетворяет требованиям конкретного приложения, можно использовать состояния ввода [[#свойства_tk_char_tk_wchar|TK CHAR/TK WCHAR]], позволяющие реализовать произвольный ввод строки. === has_input === Эта функция возвращает булево значение, сигнализирующее наличие доступных событий ввода в очереди. Возврат //true// функцией **has_input** гарантирует, что следующий вызов [[#read]] вернет результат сразу же, не блокируя выполнение программы. while (terminal_has_input()) { int key = terminal_read(); } === state === Эта функция возвращает текущее численное значение некоторого свойства/состояния библиотеки. При обработке ввода с помощью **state** запрашиваются различные своства событий ввода, см. [[#константы свойств и событий|таблицу]] ниже. int key = terminal_read(); if (key == TK_MOUSE_MOVE) { int x = terminal_state(TK_MOUSE_X); int y = terminal_state(TK_MOUSE_Y); } Следует отметить, что **state**, в общем виде, возвращет значение свойства не на момент вызова, а на момент, когда случилось последнее выбранное из очереди событие ввода, см. [[#очередь ввода]]. === check === Эта функция является простой оберткой над [[#state]]. Во многих языках программирования нельзя сравнивать числовые и булевы значения, из-за чего проверка логически булевых состояний посредством постоянного приведения численного результата [[#state]] к булевому типу может быстро надоесть. Вместо этого можно использовать **check** для проверки свойств: int key = terminal_read(); if (key == TK_Z) { if (terminal_check(TK_SHIFT)) { // 'Z': использовать жезл } else { // 'z': прочитать заклинание } } ==== Опции ввода ==== Опции ввода устанавливаются с помощью функции [[.:api:set|set]]: terminal_set ( "input:" "cursor-symbol = 0x1F," "cursor-blink-rate = 500," "precise-mouse = false," "mouse-cursor = true," "filter=[keyboard];" ); === input.cursor-symbol === Символ/тайл, используемый в качестве курсора функцией [[#read str]]. По умолчанию это "_" (символ подчеркивания) и может быть сменен, чтобы соответствовать интерфейсу приложения, например на сплошной прямоугольник или вертикальную черту. === input.cursor-blink-rate === Скорость, с которой мерцает курсор в функции [[#read str]], в миллисекундах. По умолчанию 500, т. е. дважды в секунду. === input.precise-mouse === Булев флаг, контролирующий генерацию событий о движении курсора мыши. Если он //false//, событие генерируется только если курсор перемещается с одного знакоместа на другое. Если флаг //true//, любое перемещение курсора мыши генерирует событие. По умолчанию параметр выставлен в //false//. === input.mouse-cursor === Булев флаг, контролирующий видимость курсора мыши. По умолчанию //true//. === input.sticky-close === Булев флаг, контролирующий реакцию терминала на событие TK_CLOSE. Если он //true//, единожды сгенерированное событие TK_CLOSE не может быть сброшено, а все последующие вызовы [[#read]] будут возвращать TK_CLOSE. Обычно это несколько упрощает логику приложения. Когда параметр выставлен в //false//, событие TK_CLOSE имеет смысл запроса и приложение может его проигнорировать, например спросив подтверждение у пользователя. По умолчанию параметр выставлен в //true//. === input.filter === Список событий, в которых заинтересовано приложение, все остальные события будут обработаны библиотекой автоматически и не будут прочитаны приложением. Иными словами, [[#read]] будет возвращать события только из этого списка. Список задается в виде имен событий, перечисленных через запятую внутри квадратных скобок: input.filter = [keyboard, mouse+] Именем события может быть: * группа событий: keyboard (вся клавиатура), mouse (мышь), keypad (цифровой блок-нумпад), arrows (стрелки). * имя одного события как указано в таблице ниже, но без префикса 'TK_'; регистр не имеет значения. * краткое перечисление буквенно-цифровых событий, например "abc" или "12345". Указание имени события или группы событий "включает" их чтение посредством функции [[#read]]. Если к имени добавлен знак "+", то будут докладываться как нажатия клавиши, так и отпускания. Фильтр по умолчанию -- '​keyboard'​. Для того, чтобы задействовать мышь, нужно установить фильтр в '​keyboard,​ mouse'​. "Системные" события TK_CLOSE и TK_RESIZED задействованы всегда. Если список пуст, фильтрация не производится. Для того, чтобы выключить фильтрацию, достаточно установить пустой список в качестве значения //input.filter//. ==== Константы свойств и событий ==== |< - - - - 65% >| ^Группа ^Константа ^^Описание ^ |Буквенно-цифровые |TK_A ... TK_Z ||**Событие**: сигнализирует о том, что соответствующая клавиша была нажата или отпущена. Если клавиша была отпущена, то ее коду будет добавлена констатна TK_KEY_RELEASED ([[#флаг_tk_key_released|еще...]]).\\ \\ **Свойство**: текущее состояние клавиши (см. [[#очередь ввода]]). | |::: |TK_0 ... TK_9 ||::: | |::: |TK_SPACE ||::: | |::: |TK_MINUS | ''-'' |::: | |::: |TK_EQUALS | ''='' |::: | |::: |TK_LBRACKET | ''['' |::: | |::: |TK_RBRACKET | '']'' |::: | |::: |TK_BACKSLASH | ''\'' |::: | |::: |TK_SEMICOLON | '';'' |::: | |::: |TK_APOSTROPHE | ''''' |::: | |::: |TK_GRAVE | ''`'' |::: | |::: |TK_COMMA | '','' |::: | |::: |TK_PERIOD | ''.'' |::: | |::: |TK_SLASH | ''/'' |::: | |Функциональные,\\ навигационные,\\ регистровые |TK_F1 ... TK_F12 ||::: | |::: |TK_RETURN ||::: | |::: |TK_ESCAPE ||::: | |::: |TK_BACKSPACE ||::: | |::: |TK_TAB ||::: | |::: |TK_PAUSE ||::: | |::: |TK_INSERT ||::: | |::: |TK_HOME ||::: | |::: |TK_PAGEUP ||::: | |::: |TK_DELETE ||::: | |::: |TK_END ||::: | |::: |TK_PAGEDOWN ||::: | |::: |TK_RIGHT ||::: | |::: |TK_LEFT ||::: | |::: |TK_DOWN ||::: | |::: |TK_UP ||::: | |::: |TK_SHIFT ||::: | |::: |TK_CONTROL ||::: | |Нумпад |TK_KP_0 ... TK_KP_9 ||::: | |::: |TK_KP_DIVIDE | ''/'' |::: | |::: |TK_KP_MULTIPLY | ''*'' |::: | |::: |TK_KP_MINUS | ''-'' |::: | |::: |TK_KP_PLUS | ''+'' |::: | |::: |TK_KP_PERIOD | ''.'' |::: | |::: |TK_KP_ENTER ||::: | |Мышь |TK_MOUSE_LEFT ||::: | |::: |TK_MOUSE_RIGHT ||::: | |::: |TK_MOUSE_MIDDLE ||::: | |::: |TK_MOUSE_X1 ||::: | |::: |TK_MOUSE_X2 ||::: | |::: |TK_MOUSE_MOVE ||**Событие**: генерируется, когда курсор мыши перемещается с одного знакоместа на другое (иными словами, с точностью "до знакоместа"). Если включена опция [[#input_precise_mouse|input.precise-mouse]], событие генерируется при любом движении курсора мыши. | |::: |TK_MOUSE_SCROLL ||**Событие**: генерируется при повороте колесика мыши. | |::: |TK_MOUSE_WHEEL ||**Свойство**: возвращает дельту поворота колесика мыши последнего события TK_MOUSE_SCROLL ([[#свойство_tk_mouse_wheel|еще...]]). | |::: |TK_MOUSE_X ||**Свойство**: положение курсора мыши, в знакоместах. | |::: |TK_MOUSE_Y ||::: | |::: |TK_MOUSE_PIXEL_X ||**Свойство**: положение курсора мыши, в пикселях. Эти свойства доступны вне зависимости от опции [[#input_precise_mouse|input.precise-mouse]]. | |::: |TK_MOUSE_PIXEL_Y ||::: | |::: |::: ||::: | |::: |TK_MOUSE_CLICKS ||**Свойство**: количество быстрых последовательных щелчков на момент последнего события нажатия кнопки мыши, например 1 для обычного щелчка, 2 для двойного и т. д. ([[#свойство_tk_mouse_clicks|еще...]]) | |Другие свойства |TK_WIDTH ||**Свойство**: размер терминала в знакоместах. | |::: |TK_HEIGHT ||::: | |::: |TK_CELL_WIDTH ||**Свойство**: размер знакоместа в пикселях. | |::: |TK_CELL_HEIGHT ||::: | |::: |TK_COLOR ||**Свойство**: текущий [[.:#color|цвет переднего плана]]. | |::: |TK_BKCOLOR ||**Свойство**: текущий [[.:#bkcolor|цвет фона]]. | |::: |TK_LAYER ||**Свойство**: текущий [[.:#layer|номер слоя]]. | |::: |TK_COMPOSITION ||**Свойство**: текущий [[.:#composition|флаг композиции]]. | |::: |TK_CHAR ||**Свойство**: Unicode или ANSI код символа, ассоциированного с последним прочитанным событием ввода ([[#свойства_tk_char_tk_wchar|еще...]]). | |::: |TK_WCHAR ||::: | |::: |TK_EVENT ||**Свойство**: код последнего прочитанного события ввода. | |::: |TK_FULLSCREEN ||**Свойство**: флаг активности полноэкранного режима. | |Другие события |TK_CLOSE ||**Событие**: запрос на закрытие терминала, генерируемый когда пользователь пытается закрыть окно, например щелкнув по кнопке закрытия или нажав Alt+F4 (см. опцию [[#input_sticky_close|input.sticky-close]]). | |::: |TK_RESIZED ||**Событие**: размер окна был изменен пользователем (см. опцию [[.:options#window_resizeable|window.resizeable]]). | ==== Очередь ввода ==== Все генерируемые события ввода попадают во внутреннюю очередь. Чтение этой очереди производится с помощью функции [[#read]], причем до тех пор, пока событие не будет выбрано из очереди, оно не окажет никакого воздействия на приведенные выше свойства. У такого механизма есть весьма важное последствие: все связанные со вводом свойства всегда находятся в согласованном состоянии. Например, пусть пользователь нажал A, а затем Shift+B. Будут сгенерированы следующие события: A⇩ A⇧ Shift⇩ B⇩ Shift⇧ B⇧. Когда приложение в процессе чтения очереди ввода наткнется на некоторое событие нажатия клавиши, можно будет проверить была ли нажата одна клавиша или целая комбинация простым вызовом функции [[#check]]: if (key == TK_A && terminal_check(TK_SHIFT)) { // Do something } Для события A⇩ это условие будет ложным. Для B⇩ ситуация отличается тем, что к тому моменту событие Shift⇩ уже будет выбрано и применено и [[#check]] вернет //true//. Во многих библиотеках проблема проверки нажатия комбинации клавиш решается введением целой структуры события с кодом нажатой клавиши и набором флагов состояния клавиш Shift, Ctrl, клавиш мыши и т. д. BearLibTerminal подходит к этой проблеме несколько по-иному: все связанные со вводом свойства ведут себя как одна большая структура, описывающая произошедшее событие ввода: if (key == TK_F && terminal_check(TK_SHIFT)) { // Shift+F: поиск, а не выстрел. } else if (key == TK_MOUSE_LEFT && terminal_check(TK_CONTROL)) { // Ctrl+LMB: команда, а не выбор } else if (terminal_state(TK_CHAR) == '?' && terminal_state(TK_MOUSE_Y) < 20) { // Нажатие '?' в то время как курсор мыши находится выше 20-й строки: что бы это ни было } Следует отметить, что ряд свойств терминала не зависят от очереди и всегда возвращают актуальное состояние, потому что иначе они имели бы мало смысла. Этими свойствами являются: TK_WIDTH, TK_HEIGHT, TK_CELL_WIDTH, TK_CELL_HEIGHT, TK_COLOR, TK_BKCOLOR, TK_LAYER, TK_COMPOSITION, TK_FULLSCREEN. ==== Флаг TK_KEY_RELEASED ==== Эта константа используется для различения нажатия и отпуска клавиши. Для события нажатия клавиши или автоповтора, функци [[#read]] вернет просто соответствующий код клавиши, например TK_A или TK_BACKSPACE. Для отпущенной же клавиши будет сгенерировано событие с кодом, составленным из кода клавиши и констатнты TK_KEY_RELEASED. Это позволяет легко фильровать события ввода, так как события нажатия и отпуска клавиши имеют различные коды, но при этим могут быть легко сопоставлены: int key = terminal_read(); if (key == TK_A) { // Нажато 'A' foo(); } else if (key == TK_B|TK_KEY_RELEASED) { // Отпущена 'B' bar(); } else if ((key & ~TK_KEY_RELEASED) == TK_C) { // Клавиша 'C' либо нажата, либо отпущена baz(); } Автоповтор зажатой клавиши генерирует только события нажатия, без промежуточных события отпуска клавиши. Таким образом, одному событию отпуска клавиши может соотвествовать несколько событий нажатия. ==== Свойства TK_CHAR/TK_WCHAR ==== Эти свойства предоставляют способ организации собственного ввода текста. Они возвращают код символа, ассоциированного с последним прочитанным событием ввода. TK_WCHAR возвращает код символа в Unicode, тогда как TK_CHAR -- код [[http://en.wikipedia.org/wiki/ANSI_character_set|ANSI]], транслированный в соответствии с опцией [[.:options#terminal_encoding|terminal.encoding]]. std::wstring my_read_str(int x, int y, int max) { std::wstring s; while (true) { int key = terminal_read(); if (key == TK_RETURN || key == TK_ESCAPE || key == TK_CLOSE) { // Не делаем различия между подтвеждением и отменой ввода break; } else if (key == TK_BACKSPACE && s.length() > 0) { // Удаляем один символ с конца s.resize(s.length()-1); } else if (terminal_check(TK_WCHAR) && s.length() < max) { // Добавляем символ в конец s += (wchar_t)terminal_state(TK_WCHAR); } // Визуализация ввода terminal_wprintf(x, y, L"%-*ls_", max, s.c_str()); } return s; } ==== Свойство TK_MOUSE_WHEEL ==== Это свойство возвращает направление и дельту поворота колесика мыши последнего события TK_MOUSE_SCROLL, например 1 или -2: int key = terminal_read(); if (key == TK_MOUSE_SCROLL) { int amount = terminal_state(TK_MOUSE_WHEEL); if (mouseOverFoo) { fooValue += amount; } else if (mouseOverBar) { barValue += amount; } } ==== Свойство TK_MOUSE_CLICKS ==== Это свойство возвращает количество быстрых последовательных щелчков кнопкой мыши для последнего события нажатия клавиши мыши: int key = terminal_read(); if (key == TK_MOUSE_LEFT) { int clicks = terminal_state(TK_MOUSE_CLICKS); if (clicks == 1) { // Выбрать объект под курсором мыши } else if (clicks == 2) { // Выполнить какое-либо действие с выделенным объектом. } }