TDlgBalloon для Delphi

Abstract (типа введение)

Со времени появления первых статей по использованию MS Agent в среде Delphi, многие разработчики заинтересовались применением «воздушной» выноски Balloon (шарик, баллон, пузырь). Это не случайно, выноска Balloon является удобным и
продвинутым средством как для пользователя, так и для разработчика.

Разработчику он предоставляет новый механизм взаимодействия с пользователем, который хорош позиции средства, и как сочетающегося с технологией MS Agent, и как интерфейсный элемент нового типа. Пользователь по своей природе,
в общем, настроен достаточно консервативно, но любит всяческие украшения в приложении. Кроме того, данных интерфейсный элемент позволяет реализовать более гибкое взаимодействие с пользователем. Например, по свидетельству В. Головача (Дизайн пользовательских интерфейсов, ВЛАД В. ГОЛОВАЧ) существует 4 основных фактора, являющихся критериями качества пользовательского интерфейса:

  1. Скорость выполнения работы
  2. Человеческие ошибки
  3. Обучение работе с системой
  4. Субъективное удовольствие пользователя


Так вот, выноски и диалоговые окна Balloon, позволяют эффективно решать как минимум 3 фактора. Во-первых, решают проблему корректного информирования пользователя об ошибках. С помощью Balloon можно показать небольшую выноску непосредственно около того элемента, где произошла ошибка (рис. 1).

 Ris1_nbln

Рис. 1. Выноска никогда не закрывает тот элемент, на
который указывает


Во-вторых, благодаря этому повышается скорость работы с системой, а вместе с этим, общий комфорт.

На этом введение закончим, и перейдем к конкретным решениям.

Разработки в стиле Balloon

При подготовке к данной статье я нашел ряд разработок, предоставляющих элемент управления аналогичный встроенному
Balloon из MS Office. Создают их разные программисты и фирмы. Среди них наиболее заметны следующие:

Неудобство использования 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), диалоговое окно (MessageDlgInputBox (форма для ввода
строкового значения).

Иногда, разработчикам требуются дополнительные типы выносок, скажем, типа Suggest. Выноска представляет собой форму, вырезанную по указанной картинке, например, в форме лампочки. Такой Balloon часто показывают персонажи
Office, когда хотят обратить ваше внимание на себя и дать совет (рис. 2). TipBalloon представляет собой выноску типа совета (рис. 3).

Ris2_sug

Рис. 2. Выноска типа Suggest

Ris3_tip

Рис. 3. Выноска типа TipBalloon

Кроме того, весьма функциональна выноска типа FormBalloon. Ее можно использовать в качестве средства,

выполняющего роль меню, а также получающего дополнительные данные путем непосредственного ввода (Рис. 4).

Ris4a_frm Ris4b_frm

Рис. 4. Выноски типа FormBalloon


Ну и наконец, в последнее время стали популярными выноски типа Notify (уведомление). Обычно они
указывают на некоторый интерфейсный элемент, а не на персонаж. Кроме того, он исчезает по щелчку в любом месте окна.
Notify-balloon бывают QuickNotification (быстрое уведомление) и в стиле формы. Его смысл становится ясен, если посмотреть на рисунок 5. Выноски в стиле QuickNotification свойственны подсказкам Windows XP, а NotifyForm это его расширенный вариант.

Ris5a_ntfRis5b_ntf

Рис. 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

Ris6_ArchBln

Рис. 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 персонажа уже в вызванной функции, после запуска диалогового окна, иначе все равно появляются два баллона одновременно.

Возможные недостатки и направления дальнейшей работы:

  1. После закрытия окна память из-под региона не освобождается. Подобная
    болезнь замечена во всех приложениях и Balloon’ах
    с регионами. То есть не уменьшается счетчик GDI-объектов
    в диспетчере задач (данный столбец надо включать отдельно)
  2. Пока не выработана политика решения задачи смешивания одновременного
    появления нескольких выносок. Например, если выноска модальна, то здесь нет
    проблем. Но в случае с немодальными окнами их может быть выведено несколько,
    как около персонажа так и около других контролов. Непонятно кто или что
    должно чистить за ними память. Пока разрешается запустить одно немодальное
    окно, ссылка на него будет храниться в CurrModalessBln. Следующее
    немодальное окно просто его закроет. Я не стал пока делать массив окон, чтобы потом чистить память по нескольким таймерам. Кто
    желает, может сделать это.
  3. Пока картинки не загружаются с URL
  4. Компонент существует только в виде VCL-компонента,
    неплохо бы доделать до ActiveX
  5. В дальнейшем предполагаю переписать его на 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:

Ris7_msg

Рис. 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;

Окно следующее:

Ris4b_frm

Рис. 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;

Сам пример вы уже видели, но еще раз не помешает:

Ris1_nbln

Рис. 9. Окно с быстрым уведомлением
QuickNotification


Остальные примеры вы можете найти в демонстрационном примере в конце этой страницы.

Условия распространения и использования TDlgBalloon

Если вы используйте TDlgBalloon значит вы соглашаетесь со следующими требованиями. Данный компонент распространяется бесплатно по принципу как есть («AS IS«). Компонент можно распространять бесплатно в немодифицированном виде с обязательной ссылкой на автора и его сайт (Буторин Денис — http://subritto.h1.ru, http://butorin.org. Компонент можно модифицировать в личных целях для расширения функциональности и исправления ошибок, однако, в этом случае прошу вас присылать мне модифицированный вариант. Тогда ваши нововведения окажутся доступны все остальным пользователям.

Исходный код и примеры

Самая долгожданная часть. Надеюсь, пока вы читали эту страницу вам не расхотелось скачать мой DlgBalloon.

Исходный код DlgBalloon скачивайте здесь(Закачек с 30.12.05: 1114 ).

Демонстрационный примервместе с exe-файла здесь(Закачек с 30.12.05: 1028 ).

Speak up! Let us know what you think.