====== Построение меню в программах ======
Авторы: (c) [[http://forum.sources.ru/index.php?showuser=5319|Romtek]] и [[http://forum.sources.ru/index.php?showuser=10362|volvo877]].
===== Вступление =====
Часто приходится сталкиваться с программами, в которых студенты пытались построить нечто похожее на меню, и это было нечто... (слов не подобрать :-) ). Построение меню занимало б**о**льшую часть программы и алгоритм терялся в недрах гигантских программ.
Программа состоит из набора алгоритмов, данных и интерфейса. Но ошибаются те, которые считают, что интерфейс главнее. Это большое заблужение! Потому, что **основную задачу выполняют именно __алгоритмы__**, а не украшательства. Интерфейс же совсем необязательная часть.
Более того, **неприемлемо смешивание кода алгоритмов и интерфейса**: в этом случае очень затрудняется поиск ошибок и нарушается строение программы.
===== Построение меню =====
Поэтому советую взять за основу эту программу, которая строит меню (горизонтальное или вертикальное) с управлением клавиш стрелок.\\
Выбор пункта меню ведёт к отдельной подзадаче, каждая из которых оформлена в виде процедуры. В них вы будете выполнять действия, предусмотренные вашей программой. Я привёл пример для операций над числами.
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 должна находиться **перед** построением меню. Иначе текста не будет видно!
==== Снимок экрана ====
{{:pascal:menudemo.png|Меню программы}}
==== Скачать ====
{{:pascal:menudemo.zip}}
- Menudemo.pas - программа построения меню без использования модуля.
- Menu_t.pas - программа построения меню с модулем Menu_u.\\
Menu_u.pas - модуль с основными функциями и процедурами для построения меню.
Вариант 2 более предпочтителен, т.к. за счёт использования модуля гораздо легче ориентироваться в программе и отслеживать ошибки.