Руководство - Управление множеством различных окон
В предыдущей статье мы исследовали один из способов, которым программа может поддерживать несколько экземпляров одного типа окна. В этом мы продолжим эту концепцию - разрабатываем программу, которая может поддерживать несколько экземпляров нескольких разных типов окон, в данном случае трёх:- Окно 'Button' – содержит отображаемый список, и две кнопки с надписями "Добавить" и "Удалить". При нажатии кнопки "Добавить" случайное целое число, которое даёт random добавляется в отображаемый список, при нажатии кнопки "Удалить" выделенная в данный момент запись в отображаемом списке удаляется.Каждое окно содержит строку меню с пунктами для создания нового экземпляра любого из трех поддерживаемых типов окон или закрытия текущего окна.
- Окно 'Date' – содержит отображаемый список и две кнопки аналогично окну 'Button', но также содержит Гаджет календаря, макет окна изменен для размещения этого дополнительного элемента управления. При нажатии кнопки "Добавить", в отображаемый список добавляется Дата, выделенная в данный момент в календаре.
- Окно 'Track' – содержит два ползунка со значением от 0 до 100, и строковый Гаджет. Когда ползунки перемещаются, Гаджет строки показывает значение второго ползунка, вычтенное из значения первого.
*ThisData = ActiveWindows(ThisKey) *ThisData\ListView ...
Каждая процедура знает только, как обращаться с одним типом окна, поэтому перед началом работы мы проверяем значение WindowClass, чтобы убедиться, что в качестве аргумента было представлено окно корректного типа:If *ThisData\WindowClass <> #WindowClassButton
Цикл событий тоже немного отличается. Для каждого типа события есть такое определение:; Используйте *EventGadgets \ WindowClass для вызова корректной процедуры изменения размера окна. Select *EventGadgets\WindowClass ...
Хотя выделения памяти, фактически созданные процедурами CreateWindow, будут иметь тип BUTTONWINDOW, DATEWINDOW или TRACKWINDOW, мы можем использовать *EventGadgets, потому что он определен как тип BASEWINDOW, а BASEWINDOW является родительской структурой для других структур.;- Константы #DateFormat = "%dd/%mm/%yyyy" ;- Перечисления Enumeration #WindowClassButton = 1 #WindowClassDate #WindowClassTrack EndEnumeration ; Команды меню будут одинаковыми во всех окнах. Enumeration #MenuNewButton #MenuNewDate #MenuNewTrack #MenuClose EndEnumeration ;- Структуры Structure BASEWINDOW WindowClass.i Menu.i EndStructure Structure BUTTONWINDOW Extends BASEWINDOW ListView.i AddButton.i RemoveButton.i EndStructure Structure DATEWINDOW Extends BASEWINDOW Calendar.i AddButton.i RemoveButton.i ListView.i EndStructure Structure TRACKWINDOW Extends BASEWINDOW TrackBar1.i TrackBar2.i Label.i Difference.i EndStructure ;- Переменные ; Эта хеш-карта будет отслеживать все активные окна, как и раньше, однако, поскольку структура для каждого класса ; окон различается, мы будем хранить указатели на структуры на хеш-карте, а не на ссылки на Гаджеты напрямую. NewMap ActiveWindows.i() ; Эти значения будут использоваться для придания новым окнам уникальной метки. Define.i Buttons, Dates, Tracks ; Переменные событий. ; Обратите внимание на тип *EventGadgets. Define.i Event, EventWindow, EventGadget, EventType, EventMenu, EventQuit Define.s EventWindowKey Define *EventGadgets.BASEWINDOW ;- Окно 'Button' Procedure.i CreateButtonWindow() ; Создает новое окно Button и добавляет его в хеш-карту отслеживания, ; выделяет память для ссылок на Гаджеты, создает Гаджеты ; и сохраняет эти ссылки в памяти структур. Shared Buttons, ActiveWindows() Protected *ThisData.BUTTONWINDOW Protected.i ThisWindow Protected.s ThisKey, ThisTitle ; Устанавливает заголовок окна. Buttons + 1 ThisTitle = "Button Window " + StrU(Buttons) ; Открывает окно. ThisWindow = OpenWindow(#PB_Any, 30, 30, 225, 300, ThisTitle, #PB_Window_SystemMenu - #PB_Window_SizeGadget - #PB_Window_MinimizeGadget - #PB_Window_TitleBar) ; Проверяет работу команды OpenWindow. If ThisWindow ; Установка минимальных размеров окна. WindowBounds(ThisWindow, 220, 100, #PB_Ignore, #PB_Ignore) ; Преобразует ссылку на окно в строку, которая будет использоваться в качестве значения ключа хеш-карты. ThisKey = StrU(ThisWindow) ; Выделение памяти для хранения ссылок на Гаджеты. *ThisData = AllocateMemory(SizeOf(BUTTONWINDOW)) EndIf ; Убеждаемся что память выделилась успешно. If *ThisData ; Сохранить ссылку на окно и значение указателя памяти в хеш-карте. ActiveWindows(ThisKey) = *ThisData ; Установить класс окна. *ThisData\WindowClass = #WindowClassButton ; Создать панель меню. *ThisData\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow)) ; Если создание меню удалось, создать пункты меню. If *ThisData\Menu MenuTitle("Window") MenuItem(#MenuNewButton, "New Button Window") MenuItem(#MenuNewDate, "New Date Window") MenuItem(#MenuNewTrack, "New Track Window") MenuItem(#MenuClose, "Close Window") EndIf ; Создать Гаджеты окон. *ThisData\ListView = ListViewGadget(#PB_Any, 10, 10, 200, 225) *ThisData\AddButton = ButtonGadget(#PB_Any, 15, 245, 90, 30, "Add") *ThisData\RemoveButton = ButtonGadget(#PB_Any, 115, 245, 90, 30, "Remove") Else ; Не удалось выделить память. CloseWindow(ThisWindow) EndIf ; Установить возвращаемое значение. If ThisWindow > 0 And *ThisData > 0 ; Вернуть ссылку на новое окно. ProcedureReturn ThisWindow Else ; Вернуть 0. ProcedureReturn 0 EndIf EndProcedure Procedure.i DestroyButtonWindow(Window.i) ; Удалить окно с хеш-карты ActiveWindows, освободить выделенную память, ; закрыть окно и установить флаг выхода, если это необходимо. Shared EventQuit, ActiveWindows() Protected *ThisData.BUTTONWINDOW Protected.s ThisKey ; Преобразует ссылку на окно в строку ThisKey = StrU(Window) ; Получить ссылку на указатель структуры. *ThisData = ActiveWindows(ThisKey) ; Убедиться, что был получен действительный указатель, если не получается остановить. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверить, что это корректный тип окна, если не получается остановить. If *ThisData\WindowClass <> #WindowClassButton ProcedureReturn #False EndIf ; Освободить выделенную память. FreeMemory(*ThisData) ; Удалить запись хеш-карты. DeleteMapElement(ActiveWindows(), ThisKey) ; Закрыть окно. CloseWindow(Window) ; Проверить, есть ли еще открытые окна. If MapSize(ActiveWindows()) = 0 EventQuit = #True EndIf ; Установить успешное возвращаемое значение. ProcedureReturn #True EndProcedure Procedure.i ResizeButtonWindow(Window.i) ; Изменить размеры дочерних Гаджетов в окне. Shared ActiveWindows() Protected *ThisData.BUTTONWINDOW Protected.i X, Y, W, H Protected.s ThisKey ; Получить ссылку на указатель структуры. ThisKey = StrU(Window) *ThisData = ActiveWindows(ThisKey) ; Убедиться, что был получен действительный указатель, если не получается остановить. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверить, что это корректный тип окна, если не получается остановить. If *ThisData\WindowClass <> #WindowClassButton ProcedureReturn #False EndIf ; Изменение размера списка. W = WindowWidth(Window) - 25 H = WindowHeight(Window) - 85 ResizeGadget(*ThisData\ListView, #PB_Ignore, #PB_Ignore, W, H) ; Кнопки центрирования. X = WindowWidth(Window)/2 - 95 Y = WindowHeight(Window) - 65 ResizeGadget(*ThisData\AddButton, X, Y, #PB_Ignore, #PB_Ignore) X = WindowWidth(Window)/2 + 5 ResizeGadget(*ThisData\RemoveButton, X, Y, #PB_Ignore, #PB_Ignore) ProcedureReturn #True EndProcedure Procedure.i EventsButtonWindow(Window, Gadget, Type) ; Обработка событий для окна 'button'. Shared Buttons, ActiveWindows() Protected *ThisData.BUTTONWINDOW Protected.i NewValue, Index Protected.s ThisKey ; Преобразует ссылку на окно в строку. ThisKey = StrU(Window) ; Получить ссылку на указатель структуры. *ThisData = ActiveWindows(ThisKey) ; Убедиться, что был получен действительный указатель, если не получается остановить. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверить, что это корректный тип окна, если не получается остановить. If *ThisData\WindowClass <> #WindowClassButton ProcedureReturn #False EndIf Select Gadget Case *ThisData\AddButton NewValue = Random(2147483647) AddGadgetItem(*ThisData\ListView, -1, StrU(NewValue)) Case *ThisData\RemoveButton Index = GetGadgetState(*ThisData\ListView) If Index >= 0 And Index <= CountGadgetItems(*ThisData\ListView) RemoveGadgetItem(*ThisData\ListView, Index) EndIf Case *ThisData\ListView ; Ничего не делать. EndSelect EndProcedure ;- Окно 'Date' Procedure.i CreateDateWindow() ; Создает новое окно 'Date' и добавляет его в хеш-карту отслеживания, ; выделяет память для ссылок на Гаджеты, создает Гаджеты ; и сохраняет эти ссылки в памяти структуры. Shared Dates, ActiveWindows() Protected *ThisData.DATEWINDOW Protected.i ThisWindow Protected.s ThisKey, ThisTitle Dates + 1 ThisTitle = "Date Window " + StrU(Dates) ThisWindow = OpenWindow(#PB_Any, 30, 30, 310, 420, ThisTitle , #PB_Window_SystemMenu - #PB_Window_SizeGadget - #PB_Window_MinimizeGadget - #PB_Window_TitleBar) ; Убедитесь, что команда OpenWindow открыла окно. If ThisWindow ; Установить минимальные размеры окна. WindowBounds(ThisWindow, 310, 245, #PB_Ignore, #PB_Ignore) ; Преобразуйте ссылку окна в строку, которая будет использоваться в качестве значения ключа хеш-карты. ThisKey = StrU(ThisWindow) ; Выделите память для хранения ссылок на Гаджеты. *ThisData = AllocateMemory(SizeOf(DATEWINDOW)) EndIf ; Убедимся, что память выделилась корректно. If *ThisData ; Сохранить ссылки на окна и значения указателя памяти в хеш-карте. ActiveWindows(ThisKey) = *ThisData ; Установить класс окна. *ThisData\WindowClass = #WindowClassDate ; Создать панель меню. *ThisData\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow)) ; Если создание меню удалось, создать пункты меню. If *ThisData\Menu MenuTitle("Window") MenuItem(#MenuNewButton, "New Button Window") MenuItem(#MenuNewDate, "New Date Window") MenuItem(#MenuNewTrack, "New Track Window") MenuItem(#MenuClose, "Close Window") EndIf ; Создать Гаджеты окон. *ThisData\Calendar = CalendarGadget(#PB_Any, 10, 10, 182, 162) *ThisData\AddButton = ButtonGadget(#PB_Any, 210, 10, 90, 30, "Add") *ThisData\RemoveButton = ButtonGadget(#PB_Any, 210, 45, 90, 30, "Remove") *ThisData\ListView = ListViewGadget(#PB_Any, 10, 187, 290, 200) Else ; Не удалось выделить память. CloseWindow(ThisWindow) EndIf ; Установить возвращаемое значение. If ThisWindow > 0 And *ThisData > 0 ; Вернуть ссылку на новое окно. ProcedureReturn ThisWindow Else ; Вернуть 0 ProcedureReturn 0 EndIf EndProcedure Procedure.i DestroyDateWindow(Window.i) ; Удалите окно с хеш-карты ActiveWindows, освободите выделенную память, ; закройте окно и установите флаг выхода, если это необходимо. Shared EventQuit, ActiveWindows() Protected *ThisData.DATEWINDOW Protected.s ThisKey ; Преобразует ссылку на окно в строку. ThisKey = StrU(Window) ; Получить ссылку на указатель структуры. *ThisData = ActiveWindows(ThisKey) ; Проверить, действительный ли указатель получен. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверьте, что это корректный тип окна, если не получается остановить, поскольку эта процедура не может уничтожить это окно. If *ThisData\WindowClass <> #WindowClassDate ProcedureReturn #False EndIf ; Освободить выделенную память. FreeMemory(*ThisData) ; Удалить запись хеш-карты. DeleteMapElement(ActiveWindows(), ThisKey) ; Закрыть окно. CloseWindow(Window) ; Проверить, есть ли еще открытые окна. If MapSize(ActiveWindows()) = 0 EventQuit = #True EndIf ; Установить успешное возвращаемое значение. ProcedureReturn #True EndProcedure Procedure.i ResizeDateWindow(Window.i) ; Изменение размеров дочерних Гаджетов в окне. Shared ActiveWindows() Protected *ThisData.DATEWINDOW Protected.i X, Y, W, H Protected.s ThisKey ; Получить ссылку на указатель структуры. ThisKey = StrU(Window) *ThisData = ActiveWindows(ThisKey) ; Убедиться, что был получен действительный указатель, если не получается остановить. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверить, что это корректный тип окна, если не получается остановить. If *ThisData\WindowClass <> #WindowClassDate ProcedureReturn #False EndIf ; Изменение размера списка. W = WindowWidth(Window) - 20 H = WindowHeight(Window) - 220 ResizeGadget(*ThisData\ListView, #PB_Ignore, #PB_Ignore, W, H) ProcedureReturn #True EndProcedure Procedure.i EventsDateWindow(Window, Gadget, Type) ; Обработать события для окна 'Date'. Shared Buttons, ActiveWindows() Protected *ThisData.DATEWINDOW Protected.i NewValue, Index Protected.s ThisKey ; Преобразует ссылку на окно в строку. ThisKey = StrU(Window) ; Получить ссылку на указатель структуры. *ThisData = ActiveWindows(ThisKey) ; Убедиться, что был получен действительный указатель, если не получается остановить. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверить, что это корректный тип окна, если не получается остановить. If *ThisData\WindowClass <> #WindowClassDate ProcedureReturn #False EndIf Select Gadget Case *ThisData\AddButton NewValue = GetGadgetState(*ThisData\Calendar) AddGadgetItem(*ThisData\ListView, -1, FormatDate(#DateFormat, NewValue)) Case *ThisData\RemoveButton Index = GetGadgetState(*ThisData\ListView) If Index >= 0 And Index <= CountGadgetItems(*ThisData\ListView) RemoveGadgetItem(*ThisData\ListView, Index) EndIf Case *ThisData\Calendar, *ThisData\ListView ; Ничего не делать. EndSelect EndProcedure ;- Окно 'Track' Procedure.i CreateTrackWindow() ; Создает новое окно 'Track' и добавляет его к хеш-карте отслеживания, ; выделяет память для ссылок на Гаджеты, создает Гаджеты ; и сохраняет эти ссылки в структуре памяти. Shared Tracks, ActiveWindows() Protected *ThisData.TRACKWINDOW Protected.i ThisWindow, ThisSum Protected.s ThisKey, ThisTitle Tracks + 1 ThisTitle = "Track Bar Window " + StrU(Tracks) ThisWindow = OpenWindow(#PB_Any, 30, 30, 398, 130, ThisTitle, #PB_Window_SystemMenu - #PB_Window_SizeGadget - #PB_Window_MinimizeGadget - #PB_Window_TitleBar) ; Убедимся, что команда OpenWindow смогла открыть окно. If ThisWindow ; Установить минимальные размеры окна. WindowBounds(ThisWindow, 135, 130, #PB_Ignore, 130) ; Преобразовать ссылку окна в строку, которая будет использоваться в качестве значения ключа хеш-карты. ThisKey = StrU(ThisWindow) ; Выделите память для хранения ссылок на Гаджеты. *ThisData = AllocateMemory(SizeOf(TRACKWINDOW)) EndIf ; Убедимся, что память выделилась корректно. If *ThisData ; Сохранить ссылки на окна и значения указателя памяти в хеш-карте. ActiveWindows(ThisKey) = *ThisData ; Установить класс окна. *ThisData\WindowClass = #WindowClassTrack ; Создать панель меню. *ThisData\Menu = CreateMenu(#PB_Any, WindowID(ThisWindow)) ; Если создание меню удалось, создать пункты меню. If *ThisData\Menu MenuTitle("Window") MenuItem(#MenuNewButton, "New Button Window") MenuItem(#MenuNewDate, "New Date Window") MenuItem(#MenuNewTrack, "New Track Window") MenuItem(#MenuClose, "Close Window") EndIf ; Создать Гаджеты окон. *ThisData\TrackBar1 = TrackBarGadget(#PB_Any, 10, 10, 375, 25, 0, 100, #PB_TrackBar_Ticks) *ThisData\TrackBar2 = TrackBarGadget(#PB_Any, 10, 40, 375, 25, 0, 100, #PB_TrackBar_Ticks) *ThisData\Label = TextGadget(#PB_Any, 10, 75, 80, 25, "Difference:") *ThisData\Difference = StringGadget(#PB_Any, 90, 75, 290, 25, "0", #PB_String_ReadOnly) Else ; Не удалось выделить память. CloseWindow(ThisWindow) EndIf ; Установить возвращаемое значение. If ThisWindow > 0 And *ThisData > 0 ; Вернуть ссылку на новое окно. ProcedureReturn ThisWindow Else ; Вернуть 0 ProcedureReturn 0 EndIf EndProcedure Procedure.i DestroyTrackWindow(Window.i) ; Удалите окно с хеш-карты ActiveWindows, освободите выделенную память, ; закройте окно и установите флаг выхода, если это необходимо. Shared EventQuit, ActiveWindows() Protected *ThisData.DATEWINDOW Protected.s ThisKey ; Преобразует ссылку на окно в строку. ThisKey = StrU(Window) ; Получить ссылку на указатель структуры. *ThisData = ActiveWindows(ThisKey) ; Проверить, действительный ли указатель получен. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверьте, что это корректный тип окна, если не получается остановить, поскольку эта процедура не может уничтожить это окно. If *ThisData\WindowClass <> #WindowClassTrack ProcedureReturn #False EndIf ; Освободить выделенную память. FreeMemory(*ThisData) ; Удалить запись хеш-карты. DeleteMapElement(ActiveWindows(), ThisKey) ; Закрыть окно. CloseWindow(Window) ; Проверить, есть ли еще открытые окна. If MapSize(ActiveWindows()) = 0 EventQuit = #True EndIf ; Установить успешное возвращаемое значение. ProcedureReturn #True EndProcedure Procedure.i ResizeTrackWindow(Window.i) ; Изменение размеров дочерних Гаджетов в окне. Shared ActiveWindows() Protected *ThisData.TRACKWINDOW Protected.i X, Y, W, H Protected.s ThisKey ; Получить ссылку на указатель структуры. ThisKey = StrU(Window) *ThisData = ActiveWindows(ThisKey) ; Убедиться, что был получен действительный указатель, если не получается остановить. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверить, что это корректный тип окна, если не получается остановить. If *ThisData\WindowClass <> #WindowClassTrack ProcedureReturn #False EndIf ; Изменение размера ползунков. W = WindowWidth(Window) - 20 ResizeGadget(*ThisData\TrackBar1, #PB_Ignore, #PB_Ignore, W, #PB_Ignore) ResizeGadget(*ThisData\TrackBar2, #PB_Ignore, #PB_Ignore, W, #PB_Ignore) ; Изменение размера строки. W = WindowWidth(Window) - 110 ResizeGadget(*ThisData\Difference, #PB_Ignore, #PB_Ignore, W, #PB_Ignore) ProcedureReturn #True EndProcedure Procedure.i EventsTrackWindow(Window, Gadget, Type) ; Обработка событий для окна 'Track'. Shared Buttons, ActiveWindows() Protected *ThisData.TRACKWINDOW Protected.i NewValue Protected.s ThisKey ; Преобразует ссылку на окно в строку. ThisKey = StrU(Window) ; Получить ссылку на указатель структуры. *ThisData = ActiveWindows(ThisKey) ; Убедиться, что был получен действительный указатель, если не получается остановить. If *ThisData = 0 ProcedureReturn #False EndIf ; Проверить, что это корректный тип окна, если не получается остановить. If *ThisData\WindowClass <> #WindowClassTrack ProcedureReturn #False EndIf Select Gadget Case *ThisData\TrackBar1, *ThisData\TrackBar2 NewValue = GetGadgetState(*ThisData\TrackBar1) - GetGadgetState(*ThisData\TrackBar2) SetGadgetText(*ThisData\Difference, Str(NewValue)) Case *ThisData\Label, *ThisData\Difference ; Ничего не делать. EndSelect EndProcedure ;- Главный код ; Создать первое окно. EventWindow = CreateButtonWindow() ResizeButtonWindow(EventWindow) ;- Цикл событий Repeat Event = WaitWindowEvent() EventWindow = EventWindow() EventWindowKey = StrU(EventWindow) EventGadget = EventGadget() EventType = EventType() EventMenu = EventMenu() *EventGadgets = ActiveWindows(EventWindowKey) Select Event Case #PB_Event_Gadget ; Проверить, действительный ли указатель получен. If *EventGadgets > 0 ; Используйте *EventGadgets \ WindowClass для отправки событий в корректную процедуру событий. Select *EventGadgets\WindowClass Case #WindowClassButton EventsButtonWindow(EventWindow, EventGadget, EventType) Case #WindowClassDate EventsDateWindow(EventWindow, EventGadget, EventType) Case #WindowClassTrack EventsTrackWindow(EventWindow, EventGadget, EventType) Default ; Ничего не делать EndSelect EndIf Case #PB_Event_Menu Select EventMenu Case #MenuNewButton EventWindow = CreateButtonWindow() If EventWindow > 0 ResizeButtonWindow(EventWindow) EndIf Case #MenuNewDate EventWindow = CreateDateWindow() If EventWindow > 0 ResizeDateWindow(EventWindow) EndIf Case #MenuNewTrack EventWindow = CreateTrackWindow() If EventWindow > 0 ResizeTrackWindow(EventWindow) EndIf Case #MenuClose ; Проверить, действительный ли указатель получен. If *EventGadgets > 0 ; Используйте *EventGadgets \ WindowClass для вызова правильной процедуры уничтожения окна. Select *EventGadgets\WindowClass Case #WindowClassButton DestroyButtonWindow(EventWindow) Case #WindowClassDate DestroyDateWindow(EventWindow) Case #WindowClassTrack DestroyTrackWindow(EventWindow) Default ; Ничего не делать EndSelect EndIf EndSelect Case #PB_Event_CloseWindow ; Проверить, действительный ли указатель получен. If *EventGadgets > 0 ; Используйте *EventGadgets \ WindowClass для вызова правильной процедуры уничтожения окна. Select *EventGadgets\WindowClass Case #WindowClassButton DestroyButtonWindow(EventWindow) Case #WindowClassDate DestroyDateWindow(EventWindow) Case #WindowClassTrack DestroyTrackWindow(EventWindow) Default ; Ничего не делать EndSelect EndIf Case #PB_Event_SizeWindow If *EventGadgets > 0 ; Используйте *EventGadgets \ WindowClass для вызова правильной процедуры изменения размера окна. Select *EventGadgets\WindowClass Case #WindowClassButton ResizeButtonWindow(EventWindow) Case #WindowClassDate ResizeDateWindow(EventWindow) Case #WindowClassTrack ResizeTrackWindow(EventWindow) Default ; Ничего не делать EndSelect EndIf EndSelect Until EventQuit = #True
Навигация Руководства
< Динамическая нумерация окон и Гаджетов - Обзор - Другие ключевые слова Компилятора >