Abstract (типа введение)
Со времени появления первых статей по использованию MS Agent в среде Delphi, многие разработчики заинтересовались применением «воздушной» выноски Balloon (шарик, баллон, пузырь). Это не случайно, выноска Balloon является удобным и
продвинутым средством как для пользователя, так и для разработчика.
Разработчику он предоставляет новый механизм взаимодействия с пользователем, который хорош позиции средства, и как сочетающегося с технологией MS Agent, и как интерфейсный элемент нового типа. Пользователь по своей природе,
в общем, настроен достаточно консервативно, но любит всяческие украшения в приложении. Кроме того, данных интерфейсный элемент позволяет реализовать более гибкое взаимодействие с пользователем. Например, по свидетельству В. Головача (Дизайн пользовательских интерфейсов, ВЛАД В. ГОЛОВАЧ) существует 4 основных фактора, являющихся критериями качества пользовательского интерфейса:
- Скорость выполнения работы
- Человеческие ошибки
- Обучение работе с системой
- Субъективное удовольствие пользователя
Так вот, выноски и диалоговые окна Balloon, позволяют эффективно решать как минимум 3 фактора. Во-первых, решают проблему корректного информирования пользователя об ошибках. С помощью Balloon можно показать небольшую выноску непосредственно около того элемента, где произошла ошибка (рис. 1).
Рис. 1. Выноска никогда не закрывает тот элемент, на
который указывает
Во-вторых, благодаря этому повышается скорость работы с системой, а вместе с этим, общий комфорт.
На этом введение закончим, и перейдем к конкретным решениям.
Разработки в стиле Balloon
При подготовке к данной статье я нашел ряд разработок, предоставляющих элемент управления аналогичный встроенному
Balloon из MS Office. Создают их разные программисты и фирмы. Среди них наиболее заметны следующие:
- Встроенные API в MS Office BalloonDialog от SommyTech (http://sommytech.com.ar) — 8.95 US
- BalloonMessage for Microsoft Agent (Шатрыкин Иван, Павел Сурменок) — 10 US
- MsgBalloon (Esteban Hugo Somma) — цена не известна
Неудобство использования API заключается в том, что вы становитесь зависимым от MS Office.
BalloonDialog представляет собой наиболее удачную реализацию выноски, однако, данный компонент стоит денег, да к тому же не работает в Delphi при раннем связывании (это ActiveX). Зато есть версия под .NET, это позволяет использовать в Visual Studio .NET 2002 — 2005, Borland Delphi 8, 9(2005).
Компонент BalloonMessage разработан нашими соотечественниками. Однако, версия, которой я располагал (1.1), не так функциональна, как BalloonDialog v7.1, но, думаю, дело поправимо. Последний конкурент MsgBalloon, вообще выглядит
достаточно бледно по сравнению с остальными, поэтому мы не будем на нем останавливаться. Компоненту
BalloonDialog я посвящал отдельную статью (MSAgent+MSBalloon), где описывал основные функции элемента управления на примере web.
Моя разработка — DlgBalloon
Как только я увидел компонент BalloonDialog, а особенно версии 7.1, и то, что он совершенно не работает в Delphi, то решил написать собственный компонент подобного рода. Особо над названием я фантазировать не стал и поэтому назвал просто — DlgBalloon (Ну а как еще? Без Balloon не обойтись). Решил распространять его бесплатно с исходниками, так как это простой невизуальный компонент. В конце концов, кто пожелает может обернуть его в ActiveX, тем более, что компонент пока не так хорошо отлажен.
Основные функции моего DlgBalloon:
- Поддержка основных функций всех Balloon’ов (ShowMessage, MessageDlg, InputBox, Suggest, TipBalloon,
QuickNotification, NotifyBalloon, FormBalloon) - Настройка цвета и стиля шрифта, фона, бордюра
- Добавление фоновой обоины
- Автоматическая ориентация около персонажа (ну это и так понятно, что должно быть, но все равно упомяну)
- Возможность проговаривать текст, выводимый в Balloon, с автоматическим отключением собственного
Balloon персонажа - Возможность позиционирования NotifyBalloon около необходимых элементов управления с сохранением фокуса на них. При этом в метод передается Handle контрола.
- Вывод дополнительной кнопки закрытия окна (как в новых Balloon’ах QuickNotification и NotifyBalloon)
- Возможность ручного позиционирования выноски и направления указателя
- Ну и многое другое…
Поясню некоторые понятия. Любой баллон должен поддерживать как минимум 3 основных типа выноски. Простое окно с кнопкой (ShowMessage в Delphi или MsgDlg в VB), диалоговое окно (MessageDlg)и InputBox (форма для ввода
строкового значения).
Иногда, разработчикам требуются дополнительные типы выносок, скажем, типа Suggest. Выноска представляет собой форму, вырезанную по указанной картинке, например, в форме лампочки. Такой Balloon часто показывают персонажи
Office, когда хотят обратить ваше внимание на себя и дать совет (рис. 2). TipBalloon представляет собой выноску типа совета (рис. 3).
Рис. 2. Выноска типа Suggest
Рис. 3. Выноска типа TipBalloon
Кроме того, весьма функциональна выноска типа FormBalloon. Ее можно использовать в качестве средства,
выполняющего роль меню, а также получающего дополнительные данные путем непосредственного ввода (Рис. 4).
Рис. 4. Выноски типа FormBalloon
Ну и наконец, в последнее время стали популярными выноски типа Notify (уведомление). Обычно они
указывают на некоторый интерфейсный элемент, а не на персонаж. Кроме того, он исчезает по щелчку в любом месте окна.
Notify-balloon бывают QuickNotification (быстрое уведомление) и в стиле формы. Его смысл становится ясен, если посмотреть на рисунок 5. Выноски в стиле QuickNotification свойственны подсказкам Windows XP, а NotifyForm это его расширенный вариант.
Рис. 5. Выноски Balloon в стиле NotifyForm и QuickNotification
Итак, перейдем к более подробному описанию и программированию.
Архитектура DlgBalloon
Коротко опишу внутреннее устройство DlgBalloon, чтобы пытливые умы впоследствии смогли
доработать его на благо общества.
Компонент DlgBalloon представляет собой класс TDlgBalloon, являющийся производным от TComponent. Данный класс описан в модуле AgentBalloon.pas. Другие необходимые классы:
- TBalloonForm [TForm] (форма под выноску ) — BalloonForm.pas
- TBlnSpeedButton [TCustomControl] (командные кнопки) — BlnSpeedButton.pas
- TBlnOptionButton [TCustomControl] (кнопки опций, которые обычно с лампочками) — BlnOptionButton.pas
- TBlnCheckBox [TBlnOptionButton] (CheckBox с прозрачной надписью) — BlnCheckBox.pas
- TBlnImageButton [TGraphicControl] (графическая кнопка без фокуса, например под close) — BlnImageButton.pas
Рис. 6. Вызов основных методов классов
0. При вызове любого метода для соответствующей выноски вызывается метод CreateBlnForm, который создает выноску по заданным параметрам. В качестве результата возвращает готовую, но не запущенную форму.
1. При создании выноски вызывается метод CreateNew класса TBalloonForm. В данном методе создается пустая форма нужного размера, с определенным стилем, то есть передаются значения необходимых цветов, шрифтов и т.д. На форме создаются необходимые компоненты, такие как таймеры для плавного появления и закрытия.
2. После того, как CreateBlnForm создал необходимые элементы управления на форме, он определяет стиль окна и вызывает соответствующий метод для позиционирования окна и его указателя.
Если окно обычное (не Notify), то оно позиционируется около персонажа методом AssignToCharacter.
Если окно выноски типа Notify (быстрое уведомление или форма-уведомление) запускается метод PointingNotify. В
нем происходит просчет координат треугольника под указатель в соответствии с типом указателя в параметре NotifyOrient.
3a. В методе AssignToCharacter определяются координаты персонажа, вызывается PointingOn для позиционирования окна и определения координат треугольника указателя выноски. Данные о треугольнике сохраняются в члене класса ArrowPoints.
4a. Затем вызывается метод UpdateArrow, в котором создается регион под указатель и комбинируется с общим регионом под форму.
3b. Если окно выноски типа Notify, вызывается метод PointingNotify, позиционирующий окно с уведомлением и
просчитывающий положение указателя на выноске. После PointingNotify вызываем обновление регионов UpdateArrow
Обновление позиции выноски происходит путем проверки позиции персонажа каждую секунду по таймеру. Если позиция отличается, то вызывается метод PointingOn, который позиционирует окно и изменяет координаты указателя на существующем регионе, то есть метод UpdateArrow вызывать не надо.
Все методы, за исключением Suggest, запускают метод SpeakMsg. Если включена опция SpeakingAllMsg, то
отключается собственный Balloon персонажа через функцию BalloonOn с параметром false, а затем произносится текст методом Speak. При чтении внедряются теги коррекции речи по скорости и тону, параметры для них считываются со свойств Speed и Pitch соответственно. Включается собственный Balloon персонажа уже в вызванной функции, после запуска диалогового окна, иначе все равно появляются два баллона одновременно.
Возможные недостатки и направления дальнейшей работы:
- После закрытия окна память из-под региона не освобождается. Подобная
болезнь замечена во всех приложениях и Balloon’ах
с регионами. То есть не уменьшается счетчик GDI-объектов
в диспетчере задач (данный столбец надо включать отдельно) - Пока не выработана политика решения задачи смешивания одновременного
появления нескольких выносок. Например, если выноска модальна, то здесь нет
проблем. Но в случае с немодальными окнами их может быть выведено несколько,
как около персонажа так и около других контролов. Непонятно кто или что
должно чистить за ними память. Пока разрешается запустить одно немодальное
окно, ссылка на него будет храниться в CurrModalessBln. Следующее
немодальное окно просто его закроет. Я не стал пока делать массив окон, чтобы потом чистить память по нескольким таймерам. Кто
желает, может сделать это. - Пока картинки не загружаются с URL
- Компонент существует только в виде VCL-компонента,
неплохо бы доделать до ActiveX - В дальнейшем предполагаю переписать его на C# в
Visual Studio .NET для .NET Framework.
Примеры использования
Приведу конкретные примеры использования DlgBalloon. Во многом я ориентировался на принципы
программирования в BalloonDialog, потому что для меня это основной конкурент, ну и так как это
наиболее полнофункциональный Balloon, я бы сказал Balloon’нище! Более того, тем, кто использует BalloonDialog в
Delphi (через «колено» все-таки можно прикрутить) будет проще с него перейти на мою разработку.
Инициализация компонента:
procedure TfrMain.FormCreate(Sender: TObject);
var Image1: TImage; h: integer;
begin
bln:=TDlgBalloon.Create(Self);
bln.ButtonPicture:=ImageBtn.Picture; //Прикручиваем картинки кнопок bln.OnTipClose:=Self.TipBlnClose; {Определяем что делать при закрытии Tip-окна (не обязательно)} bln.OnSuggestClick:=Self.SugBlnClick; //То же самое на Suggest-окно
Agent1.Characters.Load(‘Agent’, ‘merlin.acs’);
AChar:=Agent1.Characters.Character(‘Agent’) as IAgentCtlCharacterEx;
AChar.SoundEffectsOn:=false;
AChar.MoveTo(100, 100, 0);
AChar.Show(0);
bln.AgentObjectType:=aoAgentControl; {Определяем с чем работаем с Agent Control или Agent Server}
bln.CharacterObject:=AChar;//Указываем ссылку на интерфейс персонажа
end;
Далее запускаем необходимые выноски. Например, обычный запрос на сохранение файла с тремя русскими кнопками (по умолчанию англ.), и картинками иконок сообщений от Windows XP, которые хранятся в ImageIco (иначе будут стандартные иконки, для не XP не красиво). Заодно добавляем градиентную обоину на фон окна из Image1 и эффект затухания при появлении
и закрытии окна.
procedure TfrMain.Button1Click(Sender: TObject);
begin
bln.ButtonsCaptions:=’&Да;&Нет;&Ok;&Отмена‘;
bln.IconType:=itCustomSystem;
bln.CustomSysIcons:=ImageIco.Picture;
bln.BackgroundImage:=Image1.Picture;
bln.ShowEffect:=seFadeInOut;
bln.BackgroundImage:=Image1.Picture;
bln.MessageDlg(‘Запрос на сохранение‘, ‘Привет! Данный документ был изменен ‘+ ‘с момента последнего сохранения. Сохранить изменения?‘, mtConfirmation, mbYesNoCancel);
bln.ResetProperties;
end;
Получается следующий Balloon:
Рис. 7. Пример Balloon из листинга
Пример расширенного окна с тремя опциями, полем для ввода и комментарием. Иконки пользовательские, эффект затухания, фоновый градиент из картинки. Картинки опций из ImageList:
procedure TfrMain.Button16Click(Sender: TObject);
begin
bln.IconType:=itCustomSystem;
bln.CustomSysIcons:=ImageIco.Picture;
bln.BackgroundImage:=Image1.Picture;
bln.ShowEffect:=seFadeInOut;
bln.OptionPicture:=ImageOpt.Picture;
bln.FormBalloon.ID:=1;
bln.FormBalloon.ShowStyle:=ssModalForm;
bln.FormBalloon.Icon:=mtInformation;
bln.FormBalloon.ShowIcon:=true;
bln.FormBalloon.Title:=’Внимание‘;
bln.FormBalloon.Message:=’Выберите один из интересущющих вас вопросов или ‘+ ‘предложите свой.‘;
bln.FormBalloon.OptionButtons.Add(‘Создание приложений на Delphi с ‘+ ‘помощью OpenGL‘);
bln.FormBalloon.OptionButtons.Add(‘Создание WinAPI приложений на Delphi ‘+ ‘c поддержкой DirectX‘);
bln.FormBalloon.OptionButtons.Add(‘Создание приложений на Delphi с ‘+ ‘поддержкой технологий MS Agent и Speech API‘);
bln.FormBalloon.TextBox:=true;
bln.FormBalloon.TextBoxText:=’Запишите сюда ваш вопрос‘;
bln.FormBalloon.SepLine:=true;
bln.FormBalloon.Comment:=’Вы также можете продолжить поиск‘;
bln.FormBalloon.Buttons.Add(‘Ok‘);
bln.FormBalloon.Buttons.Add(‘Найти‘);
bln.FormBalloon.Buttons.Add(‘Отмена‘);
bln.ShowFormBalloon; bln.ResetProperties;
end;
Окно следующее:
Рис. 8. Пример расширенного окна из второго
листинга
Выноска с быстрым уведомлением, висит 10 секунд, имеет кнопку закрытия окна, позиционируется около Edit1, стрелка расположена внизу слева:
procedure TfrMain.Button13Click(Sender: TObject);
var Orient: TNotifyOrientation;
begin
Orient:=noDownLeft;
bln.BackgroundImage:=Image1.Picture;
bln.ShowEffect:=seFadeInOut;
bln.CustomSysIcons:=ImageIco.Picture;
bln.CloseButtonImage:=ImageCls.Picture;
bln.IconType:=itCustomSystem;
bln.BalloonWidth:=300;
bln.QuickNotification(‘Внимание’, ‘Число должно быть в пределах 0 до 1. ‘+ ‘Символ разделитель дробной части «‘+ DecimalSeparator+'». Для переключения раскладки воспользуйтесь ‘+ ‘комбинацией клавишь Alt+Shift.’, mtInformation, ClientToScreen(Point(Edit1.Left, Edit1.Top)), Orient, true, 10000); bln.ResetProperties;
end;
Сам пример вы уже видели, но еще раз не помешает:
Рис. 9. Окно с быстрым уведомлением
QuickNotification
Остальные примеры вы можете найти в демонстрационном примере в конце этой страницы.
Условия распространения и использования TDlgBalloon
Если вы используйте TDlgBalloon значит вы соглашаетесь со следующими требованиями. Данный компонент распространяется бесплатно по принципу как есть («AS IS«). Компонент можно распространять бесплатно в немодифицированном виде с обязательной ссылкой на автора и его сайт (Буторин Денис — http://subritto.h1.ru, http://butorin.org. Компонент можно модифицировать в личных целях для расширения функциональности и исправления ошибок, однако, в этом случае прошу вас присылать мне модифицированный вариант. Тогда ваши нововведения окажутся доступны все остальным пользователям.
Исходный код и примеры
Самая долгожданная часть. Надеюсь, пока вы читали эту страницу вам не расхотелось скачать мой DlgBalloon.
Исходный код DlgBalloon скачивайте здесь(Закачек с 30.12.05: 1114 ).
Демонстрационный примервместе с exe-файла здесь(Закачек с 30.12.05: 1028 ).