Основной критерий качества ПО - его функциональность. Но в наше время разработчики коммерческого софта должны думать и о том, что принято называть "товарным видом продукции. Как говорится, по одежке встречают, по уму провожают" - здесь в роли "одежки" выступают красивая коробка, качественно изданная документация - и, конечно же, броский и удобный интерфейс программы.
Тем, кто хочет создать яркий и нетривиальный дизайн для своего приложения, наверное, будет интересно использовать возможность создания окон непрямоугольной формы - круглых, овальных, многоугольных, звездчатых
Об окнах нестандартной формы знают многие программисты - сам автор статьи впервые узнал о них из материалов одной эхо-конференции. Почему же тогда разработчики так мало используют эту привлекательную возможность?
Попробуем дать конструктивный ответ на этот вопрос - то есть предложим технологию создания нестандартных окон.
Но сначала нужно рассказать, как получаются нестандартные окна, и какие механизмы Windows API для этого существуют. Если вы знакомы с концепцией региона окна и API-функциями региона, переходите сразу к последнему разделу статьи...
Понятие региона окна и функций региона Любое окно в Windows можно представить как прямоугольную систему координат, с точкой отсчета в левом верхнем углу. Точки экранного изображения окна описываются координатами (X, Y), где X - порядковый номер точки по горизонтальной оси, а Y - по вертикальной.
В WinAPI имеются функции для задания подмножества точек окна, путем определения координат и размеров геометрической фигуры (эллипса, многоугольника и т.п.), внутри которой эти точки расположены. Подмножество точек окна, которое эти функции умеют определять, называется регионом окна, а сами функции - функциями региона.
Имена функций региона соответствуют геометрической форме создаваемого региона: CreateEllipticRgn() - эллипс, CreateRectRgn() - прямоугольник, CreateRoundRectRgn() - прямоугольник со скругленными углами, CreatePolygonRgn() - произвольный многоугольник. Функции создают регион (т.е. описывающую его структуру в памяти) и возвращают дескриптор созданного региона (region handle).
Для одного окна можно определить любое количество разных регионов; есть также функция CombineRgn(), позволяющая получить составной регион, представляющий собой пересечение, объединение, разность или симметрическую разность нескольких регионов.
Если мы создали для нашего окна, к примеру, два региона (а значит, имеем их дескрипторы), то, запросив у этой функции операцию объединения регионов - получим новый регион, состоящий из точек, принадлежащих обоим нашим регионам - и тому, и другому (и обоим вместе, если они пересекаются).
Так, скомбинировав круглый и треугольный регионы, можно получить на экране фрагмент окна, по форме напоминающий голову в треугольной шляпе. А полученная посредством функции CombineRgn() разность двух регионов позволяет отобразить на экране фрагмент окна с самой настоящей "дырой", сквозь которую можно будет увидеть элементы заднего плана экрана, и даже пощелкать по ним мышью!
Далее, в Windows API имеется функция SetWindowRgn(), вызов которой позволяет ограничить область окна на экране определенным, созданным заранее регионом. Определив регион необычной формы, мы можем с помощью этой функции усечь прямоугольное экранное окно до его фрагмента - "куска" эллиптической, полигональной и т.п. формы.
Итак, резюмируя изложенные выше общие сведения, можно определить следующий порядок создания окна с нестандартными очертаниями:
- Создать набор элементарных регионов, составляющих нужную форму окна, вызывая соответствующие функции создания региона и запоминая дескрипторы созданных регионов;
- Если регионов больше одного, то скомбинировать созданные регионы при помощи функции CombineRgn(), получив регион с требуемыми очертаниями;
- Отобразить окно в очертаниях региона, вызвав функцию SetWindowRgn().
Описание основных функций региона Всего существует примерно два с половиной десятка функций региона. Здесь мы ограничимся описанием прототипов важнейших из них (все побробности можно узнать из любого help’а по WinAPI - например, достаточно неплохого руководства 95guide.hlp).
Прототипы функций приведены в соответствии с синтаксисом языка C++ (как в файле справки). Координаты задаются в пикселах относительно левого верхнего угла окна, также в пикселах задаются требуемые размеры. Все функции, для которых указан тип HRGN, возвращают дескриптор созданного региона.
Функция создания прямоугольного региона: HRGN CreateRectRgn(int nLeftRect, // X-координата верхнего левого угла прямоугольникаint nTopRect, // Y-координата верхнего левого угла прямоугольникаint nRightRect, // X-координата нижнего правого угла прямоугольникаint nBottomRect // Y-координата нижнего правого угла прямоугольника) Для описания прямоугольника достаточно указать координаты его левой верхней и правой нижней вершин. Функция создания эллиптического региона: HRGN CreateEllipticRgn(int nLeftRect,int nTopRect,int nRightRect,int nBottomRect); Здесь задаются координаты прямоугольника, в который вписывается требуемый эллипс. Функция создания региона - произвольного многоугольника: HRGN CreatePolygonRgn(CONST POINT *lppt, // указатель на массив координат вершинint cPoints, // число элементов массива вершинint fnPolyFillMode // режим заполнения многоугольника); Прежде чем вызывать эту функцию, нужно создать и заполнить массив координат вершин многоугольника. Элементы этого массива - структуры типа POINT: typedef struct tagPOINT {LONG X,LONG Y,} POINT;Указатель на такой массив передается в функцию первым параметром. Вторым параметром передается число вершин многоугольника. Третий параметр особого значения не имеет; может, например, всегда задаваться константой WINDING. Функция создания региона - прямоугольника со скругленными углами: HRGN CreateRoundRectRgn(int nLeftRect, // координаты прямоугольникаint nTopRect, // ...int nRightRect, // ...int nBottomRect, // ...int nWidthEllipse, // ширина скругляющего эллипсаint nHeightEllipse // высота скругляющего эллипса); Здесь, кроме координат прямоугольника, нужно задать размеры осей эллипса, который будет применяться для закругления углов. Функция комбинирования регионов: int CombineRgn(HRGN hrgnDest, // дескриптор результирующего регионаHRGN hrgnSrc1, // дескриптор первого региона-операндаHRGN hrgnSrc2, // дескриптор второго региона-операндаint fnCombineMode // комбинирующая операция); Эта функция создает регион путем выполнения указанной теоретико-множественной операции над множествами точек двух регионов-операндов. Переменная hrgnDest, в которую будет помещен дескриптор нового региона, должна быть описана в программе заранее. Возможны следующие операции (задаваемые соответствующими константами): RGN_AND: пересечение двух регионов RGN_COPY: копия региона, заданного переменной hrgnSrc1RGN_DIFF: разность двух регионов RGN_OR: объединение двух регионов RGN_XOR: симметрическая разность двух регионов Функция возвращает код результата, который представляет собой одну из следующих констант: NULLREGION: результирующий регион пуст SIMPLEREGION: результирующий регион есть прямоугольник COMPLEXREGION: результирующий регион есть сложная фигура ERROR: результирующий регион не был создан Функция задания региона окна: int SetWindowRgn(HWND hWnd, // дескриптор окнаHRGN hRgn, // дескриптор регионаBOOL bRedraw // флаг перерисовки); Функция устанавливает для окна, заданного первым параметром, регион, заданный вторым параметром. Логический параметр bRedraw указывает, должно ли окно быть перерисовано немедленно в очертаниях нового региона.
Этих шести функций достаточно для создания сложных регионов и вывода на на экран окон с соответствующими силуэтами. Из функций управления внешним видом региона можно выделить следующие две. Функция закраски региона: BOOL FillRgn(HDC hdc, // дескриптор контекста устройстваHRGN hrgn, // дескриптор регионаHBRUSH hbr // дескриптор кисти); Функция закрашивает заданный регион, пользуясь параметрами заданной "кисти" (объекта brush). Если все получилось - функция возвращает True, иначе - False. Функция обрамления региона: BOOL FrameRgn(HDC hdc, // дескриптор контекста устройстваHRGN hrgn, // дескриптор регионаHBRUSH hbr, // дескриптор кистиint nWidth, // ширина обрамленияint nHeight // высота обрамления); Функция окаймляет регион "бордюром" заданной ширины и высоты, в соответствии с параметрами кисти.
В Delphi для доступа к функциям регионов и связанных с ними типам и константам необходимо подключить модуль Windows (uses Windows;). Ниже приводится пример метода - обработчика события создания формы для Delphi, которой выполняет установку нестандартного региона для окна формы. При запуске приложения на экране появляется не обычное прямоугольное окно, а неправильный четырехугольник с круглым отверстием посередине: procedure TForm1.FormCreate(Sender: TObject); varrgn1,rgn2:HRGN;v:array [1..4] of TPoint; // массив вершин beginv[1].X:=40; v[1].Y:=0;v[2].X:=200; v[2].Y:=20;v[3].X:=300; v[3].Y:=150;v[4].X:=10; v[4].Y:=180; rgn1:=CreatePolygonRgn(v,4,WINDING); rgn2:=CreateEllipticRgn(100,60,150,110); CombineRgn(rgn1,rgn1,rgn2,RGN_DIFF); SetWindowRgn(Handle,rgn1,True); DeleteObject(rgn1); // (ОсвобождаемDeleteObject(rgn2); // ресурсы) end;
Ну и что? Это, похоже, тот вопрос, который задавали себе все программисты, ознакомившиеся с функциями региона. В самом деле, какую практическую ценность может иметь для меня возможность вырезать из прямоугольного окна лоскут с дыркой посередине? Вид такого "окна" способен скорее вызвать отвращение, чем привлечь внимание к моему программному продукту. Вот если бы мы умели создать регион по контуру имеющейся у нас красивой картинки - это было бы здорово! На экране появлялось бы окно в форме этой картинки, обрезанное точно по ее контуру, и это было бы в самом деле эффектно... Пользователь бы видел на экране, скажем, компакт-диск, или магнитолу современного обтекаемого дизайна - и это был бы не просто рисунок, а живое окно с органами визуального управления!
Но как это сделать? Ведь нужно определить координаты и размеры многочисленных "прямоугольников", "эллипсов" и "многоугольников", из которых в совокупности состоит силуэт нашей картинки, чтобы затем создать соответствующие им регионы и скомбинировать их... Наверное, именно поэтому разработчики редко используют нестандартные окна...
На мой взгляд, единственное практичное решение - создать программу,автоматизирующую создание региона по контурурисунка; позволяющей использовать визуальную технику (подобно графическим редакторам) для наложения нажелаемый рисунок графических примитивов.
Николай Больсунов
Источник - http://delphiclub.ru
|