Часто приходится сталкиваться с программами, в которых студенты пытались построить нечто похожее на меню, и это было нечто… (слов не подобрать ). Построение меню занимало большую часть программы и алгоритм терялся в недрах гигантских программ.
Программа состоит из набора алгоритмов, данных и интерфейса. Но ошибаются те, которые считают, что интерфейс главнее. Это большое заблужение! Потому, что основную задачу выполняют именно алгоритмы, а не украшательства. Интерфейс же совсем необязательная часть.
Более того, неприемлемо смешивание кода алгоритмов и интерфейса: в этом случае очень затрудняется поиск ошибок и нарушается строение программы.
Поэтому советую взять за основу эту программу, которая строит меню (горизонтальное или вертикальное) с управлением клавиш стрелок.
Выбор пункта меню ведёт к отдельной подзадаче, каждая из которых оформлена в виде процедуры. В них вы будете выполнять действия, предусмотренные вашей программой. Я привёл пример для операций над числами.
uses crt; Type MenuType = (Vertical, Horizontal); const width = 9; { Максимальная длина элементов (букв) меню } Items1 = 5; { Количество элементов меню 1 } optText1: array [0..Items1-1] of string = ('Сложение', 'Вычитание', 'Деление', 'Умножение', 'Выход'); Items2 = 3; { Количество элементов меню 2 } optText2: array [0..Items2-1] of string = ( 'item 1', 'item 2', 'item 3' ); optNormal = LightGray; { цвет невыделенных элементов меню } optSelected = Yellow; { цвет выделенных элементов меню } var X, Y, selected, { Индекс элемента, который будет подсвечиваться в начале программы } row: integer; _style: menuType; { Указывает на тип меню: вертикальный (Vertical) или горизонтальный (Horizontal) } { Эта процедура используется процедурой MenuOption для построения меню } procedure MakeMenu (optText: array of string; MaxItems: integer); var i, _X: byte; begin Y := row; _X := X; for i := 0 to MaxItems-1 do begin GoToXY (_X, Y); if i = selected then TextColor (optSelected) else TextColor (optNormal); write (optText[i]); If _style = Horizontal Then inc (_X, width + 1) Else inc (Y, 2); end; end; { выбираем нужный элемент меню этой функцией } function MenuOption (optText: array of string; MaxItems: integer): byte; var ch: char; begin selected := 0; If _style = Vertical then begin X := (80 - width) div 2; row := (25 - MaxItems) div 2; End Else Begin X := (80 - MaxItems * width) div 2; row := 2; { строчка, в которой будет находиться горизонтальное меню } GotoXY(1, row); ClrEol; { ... а для горизонтального - только строку где будет меню. } End; repeat MakeMenu (optText, MaxItems); ch := readkey; if ch = #0 then ch := readkey; case ch of #80, #77: {Down/Right} begin inc (Selected); if Selected = MaxItems then Selected := 0; MakeMenu (optText, MaxItems); end; #72, #75: {Up/Left} begin dec (Selected); if Selected < 0 then Selected := MaxItems-1; MakeMenu (optText, MaxItems); end; end; until ch = #13; {Enter} MenuOption := Selected + 1; TextColor (optNormal); If _style = Vertical Then clrscr; end; procedure Add; begin end; procedure Subtract; begin end; procedure Divide; begin end; procedure Multiply; begin end; var Option: byte; { номер выбранного пункта } begin clrscr; _style := Vertical; { вертикальное меню } Option := MenuOption (optText1, Items1); case option of 1: Add; { сложить числа } 2: Subtract; { вычесть числа } 3: Divide; { поделить числа } 4: Multiply; { умножить числа } 5: exit; { Выйти из программы } end; { вывести (если нужно) номер выбранного пункта } writeln ('Номер пункта: ', option); readln; _style := Horizontal; { горизонтальное меню } Option := MenuOption (optText2, Items2); end.
Бывает, что есть необходимость добавить пояснительный текст рядом с меню. В подавляющем количестве студенты пишут тогда следующий код:
begin GotoXY (1,2); writeln ('Для управления пользуйтесь клавишами стрелок:'); GotoXY (1,4); writeln ('1 - сделать то-то...'); writeln ('2 - сделать то-то...'); writeln ('3 - сделать то-то...'); writeln ('4- сделать то-то...'); writeln ('5 - сделать то-то...'); end;
Особенно неудобно в программах с большим текстом кода. В нём можно просто утонуть. Это дико неудобно возиться со множеством writeln, которые только отвлекают от сути, в то время как можно загрузить текст из текстового файла, заранее отредактированного в любом текстовом редакторе такой текст: Для управления пользуйтесь клавишами стрелок:
1 - сделать то-то... 2 - сделать то-то... 3 - сделать то-то... 4 - сделать то-то... 5 - сделать то-то...
Не правда ли, удобнее?
Теперь рассмотрим процедуру, которая загрузит текст из отдельного текстового файла.
{ Эта процедура используется процедурой TextWindow для загрузки текста из отдельного текстового файла. }
procedure LoadText (fname: string); var F: text; str: string; begin Assign (F, fname); {$I-} Reset (F); {$I+} if IOresult = 0 then while Not EOF (F) do begin readln (F, str); writeln (str); end; Close (F); end;
Эта процедура выведет текст в заданном регионе (окне) из текстового файла fn.
{ Выводит текст в заданном регионе из текстового файла fn } procedure TextWindow (x1, y1, x2, y2: integer; fn: string); const TextColor = Cyan; { цвет фона региона } begin Window (x1, y1, x2, y2); TextBackground (TextColor); ClrScr; LoadText (fn); Window (1, 1, 80, 25); TextBackground (Black); end;
Таким образом, задав нужные вам параметры, вы увидите текст рядом с меню. Например,
TextWindow (2, 2, 78, 7, 'strelki.txt'); _style := Vertical; { вертикальное меню } Option := MenuOption (optText1, Items1);
Здесь текст выводится в окне с границами 2, 2, 78, 7 из файла 'strelki.txt'.
uses crt, menu_u; const width1 = 9; { Максимальная длина элементов (букв) меню } Items1 = 5; { Максимальное количество элементов меню 1 } optText1: array [0..Items1-1] of string = ('Сложение', 'Вычитание', 'Деление', 'Умножение', 'Выход'); width2 = 6; { Максимальная длина элементов (букв) меню } Items2 = 3; { Максимальное количество элементов меню 2 } optText2: array [0..Items2-1] of string = ( 'item 1', 'item 2', 'item 3' ); procedure Add; begin end; procedure Subtract; begin end; procedure Divide; begin end; procedure Multiply; begin end; var Option: byte; { номер выбранного пункта } begin clrscr; TextWindow (2, 2, 78, 7, 'menu.txt'); _style := Vertical; { вертикальное меню } Option := MenuOption (optText1, Items1, width1); case option of 1: Add; { сложить числа } 2: Subtract; { вычесть числа } 3: Divide; { поделить числа } 4: Multiply; { умножить числа } 5: exit; { Выйти из программы } end; { вывести (если нужно) номер выбранного пункта } writeln ('Номер пункта: ', option); readln; _style := Horizontal; { горизонтальное меню } Option := MenuOption (optText2, Items2, width2); end.
За построение меню отвечает процедура MakeMenu - её вызывает функция MenuOption, которая возвращает результат в виде индекса выбранного пункта.
Стиль меню определяется значением _style (Vertical/Horizontal) - то, как меню будет располагаться на экране.
_style := Vertical; { вертикальное меню }
Цвет нормальных элементов определяется константой optNormal, а цвет выделенных - optSelected.
Остаётся всего-лишь определить названия элементов меню в optText, константу Items (количество элементов меню), константу width (максимальная длина букв самой длинной строки меню) и меню готово!
Для вывода пояснительного текста рядом с меню можно воспользоваться процедурой TextWindow. Пять параметров означают координаты окна и название файла, из которого загрузится описание для меню.
Процедура TextWindow должна находиться перед построением меню. Иначе текста не будет видно!
Menu_u.pas - модуль с основными функциями и процедурами для построения меню.
Вариант 2 более предпочтителен, т.к. за счёт использования модуля гораздо легче ориентироваться в программе и отслеживать ошибки.