5.4 Меню с перемещением курсора
Основное отличие этого варианта меню от простого заключается в способе ввода переменной sel_menu. Если в простом меню она вводится непосредственно пользователем, то в данном варианте ввод осуществляется косвенно с помощью клавиш управления курсором. Выбор пункта можно организовать следующим образом:
void highlight_menu(int item,int highlight);
void run()
{
//...
int selected=0;
int sel_menu=0;
do
{
highlight_menu(sel_menu,1); //подсвечиваемпунктменю
int key=getch();
highlight_menu(sel_menu,0); //временногасимпунктменю
switch (key)
{
case0:
key=getch();
switch (key)
{
case UP:
sel_menu=(sel_menu-1+max_menu)%max_menu;
break;
case DOWN:
sel_menu=(sel_menu+1)%max_menu;
break;
}
break;
case ENTER:
selected=1;
break;
}
} while (!selected);
//...
}
Вприведенномфрагментекодафункцияhighlight_menu выводитэлементменюсзаданнымномеромсвключенной (highlight != 0) или выключенной (highlight = = 0) подсветкой. Данную функцию можно реализовать различными способами. Как правило, подсветка производится с помощью установки соответствующих атрибутов текста или вызовом функций highvideo и lowvideo. В зависимости от реализации в highlight_menu может потребоваться передача дополнительных параметров, описывающих меню (например, массив строк с названиями пунктов, размеры меню на экране и т.п.).
5.5 Ввод строки с редактированием
Стандартные средства Си для ввода данных (функция scanf) предоставляют пользователю очень мало возможностей. Редактирование вводимого текста возможно только путем удаления символов с конца строки, начальное значение установить нельзя (по умолчанию вводится пустая строка), прокрутки строки, если она не влазит в окно, не предусмотрено (точнее, строка просто переносится по границе окна, что не всегда желательно).
В связи с этим очень часто возникает необходимость в разработке своих средств ввода текста, которые обеспечили бы удобный и привычный пользовательский интерфейс. При этом достаточно организовать ввод текстовой строки, а ввод чисел и более сложных значений (например, интервалов) организуется на основе текстового ввода с последующей обработкой введенной строки (например, чтением числа с помощью функции strtol).
Рассмотрим следующую функцию ввода текста с редактированием:
int inputstr(char* str,int maxlen)
{
int x=wherex(),y=wherey(),curlen=strlen(str);
int pos=curlen;
char firstkey=1;
highvideo();
cprintf("%-*s",maxlen,str);
lowvideo();
gotoxy(x+pos,y);
_setcursortype(_NORMALCURSOR);
int exitcode=-1;
while (exitcode<0)
{
int key=getch();
switch (key)
{
case 0:
key=getch();
switch (key)
{
case LEFT:
if (pos>0)
--pos;
break;
case RIGHT:
if (pos<curlen)
++pos;
break;
case DELETE:
if (pos<curlen)
movmem(str+pos+1,str+pos,curlen-pos);
break;
case HOME:
pos=0;
break;
case END:
pos=curlen;
break;
}
break;
case BACKSPACE:
if (pos>0)
{
--pos;
movmem(str+pos+1,str+pos,curlen-pos);
}
break;
case ESC:
exitcode=0;
break;
case ENTER:
exitcode=1;
break;
default:
if (key>=' ')
{
if (firstkey)
{
pos=0;
str[0]=0;
}
if (curlen<maxlen)
{
movmem(str+pos,str+pos+1,curlen-pos+1);
str[pos]=key;
++pos;
}
}
}
curlen=strlen(str);
firstkey=0;
gotoxy(x,y);
cprintf("%-*s",maxlen,str);
gotoxy(x+pos,y);
}
_setcursortype(_NOCURSOR);
gotoxy(x,y);
return exitcode;
}
Данная функция организует ввод строки ограниченной длины с возможностью перемещения курсора клавишами Left, Right, Home, End и удаления символов клавишами Delete и Backspace. Ввод завершается при нажатии Enter либо Esc, при этом возвращаемое значение показывает, какая из клавиш была нажата. Это позволяет в вызывающей функции определить, отказался пользователь от ввода или подтвердил его. Если первая нажатая клавиша приводит к вводу символа, то старое содержимое строки очищается. Такая особенность позволяет легко вводить новые данные вместо старых. В то же время, если первой нажатой клавишей была клавиша редактирования, старое содержимое сохраняется.
Перед началом ввода функция устанавливает нормальный вид курсора, а после окончания ввода скрывает текстовый курсор. Это позволяет использовать функцию в интерфейсах-меню и интерфейсах со свободной навигацией, не прибегая к дополнительным вызовам _setcursortype.
При редактировании строки ее содержимое в буфере str перемещается функцией movmem, которая предназначена для копирования участка памяти. Для использования этой функции необходимо подключить заголовочный файл <mem.h>.
Приведенная функция inputstr может использоваться в качестве замены scanf, при этом интерфейс становится более дружественным, что благоприятно сказывается на впечатлении пользователя от программы. Для ввода чисел различных типов можно написать отдельные функции, преобразующие число в строку, вызывающие inputstr и затем преобразующие полученную строку обратно в число.
Написать функцию, получающую байт атрибутов и возвращающую 0, если цвета фона и тона в этом байте совпадают, и ненулевое значение, если они различны.
Разработать описание пользовательского интерфейса для программы «Будильник».
Исходными данными для программы являются:
- время, на которое установлен будильник;
- флажок, показывающий, включен будильник или выключен;
- период повторения будильника: однократное срабатывание, включение каждый день, включение раз в неделю.
Для программы требуется разработать консольный интерфейс, интерфейс-меню и интерфейс со свободной навигацией. Результатом разработки является текстовое описание запросов, пунктов меню или компонентов интерфейса соответственно.
7.1 Решение квадратного уравнения
Написать программу квадратного уравнения, имеющую консольный интерфейс или интерфейс, основанный на простом меню. Все коэффициенты уравнения вводятся пользователем. При вводе коэффициентов должна предоставляться возможность редактирования вводимого значения.
Если уравнение имеет один корень, то нужно выводить только одно значение. Если уравнение не имеет корней, выводится соответствующее сообщение.
Замечание: при написании программы необходимо обращать внимание на отделение пользовательского интерфейса от вычислительной части.
7.2 Построение графика произвольной функции
Написать программу, строящую график функции, выбранной пользователем. Выбор функции осуществляется из некоторого фиксированного списка жестко заданных функций. Кроме того, программа должна предоставлять возможность ввода функции с клавиатуры.
Помимо выбора функции, требуется организовать ввод координат области, для которой будет строиться график.
Замечание: парсинг введенного текста можно реализовать рекурсией.
8.1 Элемент управления «Список»
Реализовать программно элемент управления «Список с прокруткой» для текстового режима. Работа со списком должна сводиться к вызову одной функции, в которую передаются координаты области на экране, которую занимает список, и массив строк, составляющих список. Функция самостоятельно организует интерфейс для выбора элемента.
Каждая строка представляет один элемент списка. В строках не должно быть символов возврата каретки и перевода строки. Элементы должны выводиться в заданном окне. Количество строк в списке может как превышать высоту окна, так и быть меньше ее. В первом случае необходимо предусмотреть прокрутку содержимого в окне, во втором – обеспечить корректный выбор только тех позиций в окне, которые содержат строки списка (другими словами, курсор не должен выходить за пределы и окна, и списка).
Текущий элемент подсвечивается каким-либо цветом, заданным разработчиком. Выбор элемента осуществляется нажатием клавиш «Up» (перемещение курсора вверх) и «Down» (перемещение курсора вниз).
Подтверждение выбора (и выход из функции) осуществляется при нажатии клавиш Enter, Esc, Left, Right, Tab, Shift+Tab. Каждой из этих клавиш должен соответствовать определенный код, который также возвращается функцией выбора из списка. Этот код может использоваться вызывающей функцией для того, чтобы определить дальнейшее действие: переход к предыдущему (Shift+Tab, Left) или следующему (Tab, Right) элементу управления, выполнение основного действия программы (Enter), выход из программы (Esc).
Помимо указанного кода функция должна возвращать индекс выбранного элемента списка.
8.2 Ввод строки с редактированием и прокруткой
Написать функцию ввода текстовой строки с редактированием. При вызове функции ей передается длина поля ввода (максимальная длина отображаемой на экране части строки), а также координаты первого символа поля ввода на экране. Поле ввода однострочное, т.е. имеет высоту, равную 1 знакоместу.
Длина вводимой строки может превышать размеры поля ввода. Если в процессе ввода или перемещения курсора по строке текущая позиция ввода выходит за пределы поля, необходимо производить прокрутку строки в окне.
По усмотрению разработчика максимальная длина строки может либо передаваться в функцию, либо определяться объемом доступной динамической памяти. В первом случае буфер под строку резервирует вызывающая функция, во втором – функция ввода.
Функция ввода должна предоставлять следующие возможности по редактированию текста: перемещение курсора, ввод символа, удаление символа над курсором (клавиша Delete), удаление символа перед курсором (клавиша Backspace).
Если часть строки находится за пределами поля, желательно показывать это каким-либо образом (например, ставить выделенные цветом символы «<» и «>» в начале и конце поля ввода, если есть текст за пределами поля с соответствующей стороны).
1. Иванова Г.С. Технология программирования: Учебник для вузов. / Г.С. Иванова. М.: Изд-во МГТУ им. Н.Э. Баумана, 2002.
2. Фролов А., Фролов Г. Программирование видеоадаптеров / А. Фролов, Г. Фролов. М.: Диалог-МИФИ, 1993.
3. Подбельский В.В. Программирование на языке Си. Учеб. пособие. / В.В. Подбельский, С.С. Фомин, М.: Финансы и статистика, 2004. – 600 с.
4. Керниган Б. Язык программирования Си / Б. Керниган, Д. Ритчи. М.: Финансы и статистика, 1992. – 272 с.