Инспектор объектов и метаданные
Как уже было сказано, класс TGsvObjectInspectorTypeListInfo предоставляет дополнительную функциональность при работе со свойствами - перечислимыми типами. Класс переопределяет методы IntegerToString, StringToInteger и FillList, а для задания списка перечислений вводит новый виртуальный метод ListEnumItems - этот метод напоминает ChildrenInfo базового класса, но возвращает не типовые метаданные, а свойства каждого элемента перечисления - его имя и ассоциированное с ним значение - эти параметры определены записью TGsvObjectInspectorListItem. Конкретный метакласс, описывающий свойства-перечисления может быть порожден от класса TGsvObjectInspectorTypeListInfo, причем достаточно будет переопределить только метод ListEnumItems. Метод FillList выполняет итерацию по всем перечислимым значениям, вызывая ListEnumItems с монотонно возрастающим индексом до тех пор, пока ListEnumItems не вернет значение nil. Результаты итерации передаются визуальному компоненту инспектора через параметр List. Для преобразования строкового вида значения перечисления к целочисленному виду и для обратного преобразования служат методы StringToInteger и IntegerToString, алгоритм которых очень похож - оба они итерируют список перечислений, но в первом случае критерием для поиска является строковое имя, а во втором случае - ассоциированное с ним значение. Очевидно, что такой базовый класс может быть использован для любых перечислимых типов, причем даже таких, в которых значения перечисления не образуют упорядоченную монотонную последовательность.
TGsvObjectInspectorTypeFontInfo = class(TGsvObjectInspectorTypeInfo)
public
class procedure ShowDialog(Inspector: TComponent;
Info: PGsvObjectInspectorPropertyInfo;
const EditRect: TRect); override;
class function ObjectToString(const Value: TObject):
String; override;
end;
class procedure TGsvObjectInspectorTypeFontInfo.ShowDialog(
Inspector: TComponent;
Info: PGsvObjectInspectorPropertyInfo; const EditRect: TRect);
var
dlg: TFontDialog;
fnt: TFont;
begin
if not Assigned(Info) then
Exit;
if not Assigned(Info^.NestedObject) then
Exit;
if not (Info^.NestedObject is TFont) then
Exit;
fnt := TFont(Info^.NestedObject);
dlg := TFontDialog.Create(Inspector);
try
dlg.Font.Assign(fnt);
if dlg.Execute then
fnt.Assign(dlg.Font);
finally
dlg.Free;
end;
end;
class function TGsvObjectInspectorTypeFontInfo.ObjectToString(
const Value: TObject): String;
begin
if Assigned(Value) then
if Value is TFont then
with TFont(Value) do
Result := Format('%s, %d', [Name, Size]);
end;
Класс TGsvObjectInspectorTypeFontInfo демонстрирует способ создания метакласса для специфического редактора свойства, в данном случае, для свойства-шрифта, имеющего тип TFont. Здесь переопределяются два метода - ShowDialog и ObjectToString. Методу ShowDialog передаются три аргумента: ·Inspector - родительский компонент для формы-диалога, ·Info - метаданные свойства, ·EditRect - прямоугольник, представляющий собой экранные координаты поля редактирования визуального компонента инспектора. Эти координаты можно использовать для того, чтобы расположить диалог, скажем, прямо под значением редактируемого свойства (подобно списку). Конечно, это имеет смысл только для небольших по размеру диалогов.
Для свойств, отображающих диалог, менеджер заполняет поле метаданных NestedObject - оно указывает на инспектируемый объект или его заместитель. В данном случае менежер увидит, что свойство-шрифт является объектом-классом и определит его адрес, используя адрес объекта верхнего уровня в дереве объектов-свойств и имя свойства. Если бы это было простое свойство, например, TColor, то менеджер заполнил бы поле NestedObject указателем на объект текущего уровня.
После того, как мы определили, что инспектируемое свойство действительно является объектом нужного нам типа (в данном случае TFont), мы создаем диалог, инициализируем его данные текущим значением свойства, отображаем диалог и при успешном завершении переносим новое значение свойства в инспектируемый объект.
Другой метод класса - ObjectToString определяет то, как будет выглядеть значение свойства в инспекторе. В данном случае мы считаем, что основные свойства шрифта - это его имя и размер. Такой способ отображения отличается от того, что мы видим в инспекторе Delphi - в качестве значения объекта Delphi отображает имя его типа.
Визуальный компонент инспектора
В этом разделе мы рассмотрим визуальный компонент инспектора, его основные методы и события, а также некоторые пользовательские аспекты, какие, как хинты. Причем, для простоты опустим аспекты реализации и, кроме того, будем использовать понятия "инспектор" и "визуальный компонент инспектора" как синонимы.
Как это принято в Delphi, визуальный компонент представлен в двух формах - как TGsvCustomObjectInspectorGrid и, соответственно, TGsvObjectInspectorGrid. Опуская детали реализации и не очень важные свойства, класс инспектора определяется так:
TGsvCustomObjectInspectorGrid = class(TCustomControl)
protected
property LongTextHintTime: Cardinal;
property LongEditHintTime: Cardinal;
property AutoSelect: Boolean;
property HideReadOnly: Boolean;
property OnEnumProperties:
TGsvObjectInspectorEnumPropertiesEvent;
property OnGetStringValue:
TGsvObjectInspectorGetStringValueEvent;
property OnSetStringValue:
TGsvObjectInspectorSetStringValueEvent;
property OnGetIntegerValue:
TGsvObjectInspectorGetIntegerValueEvent;
property OnSetIntegerValue:
TGsvObjectInspectorSetIntegerValueEvent;
property OnFillList:
TGsvObjectInspectorFillListEvent;
property OnShowDialog:
TGsvObjectInspectorShowDialogEvent;
property OnHelp: TGsvObjectInspectorInfoEvent;
property OnHint: TGsvObjectInspectorInfoEvent;
public
procedure NewObject;
procedure Clear;
procedure ExpandAll;
procedure CollapseAll;
end;
Вначале отметим самые простые свойства и методы: ·AutoSelect - если AutoSelect установить в True, то при выборе свойства, доступного для редактирования весь его текст будет выделяться, ·HideReadOnly - если установить в True, то инспектор будет скрывать все свойства, доступные только по чтению, ·Clear - вызов этого метода очистит инспектор, что означает отсутствие инспектируемого объекта, ·ExpandAll - раскрыть все вложенные веточки дерева свойств, ·CollapseAll - свернуть все вложенные веточки.
Цикл событий инспектора при инспектировании начинается с вызова метода NewObject. Это приведет к тому, что инспектор начнет циклически вызывать событие OnEnumProperties. Сигнатура обработчика этого события следующая:
Index: Integer; out Info: PGsvObjectInspectorPropertyInfo) of object;
Обработчику передается монотонно увеличивающееся значение Index и, при каждом обращении, обработчик должен вернуть в out-аргументе указатель на метаданные очередного свойства или nil, если все свойства перечислены. Обработчик может выглядеть так:
out Info: PGsvObjectInspectorPropertyInfo);
begin
Info := ObjectManager.PropertyInfo(Index);
end;
Отправить комментарий