Значки Icons8, окно «О программе» с указанием авторства

Значки заменены на присланные. Они вшиты в программу модулем uIconData,
который собирается из icons_online командой tools/make_icons.py — правятся
исходные картинки, а не он. Размер выбран под кнопку: двадцать четыре
точки в клетке двадцать восемь, уменьшение делает convert, пропорции
сохранены — все исходники квадратные.

Цвет значку задаётся при отрисовке: берётся прозрачность точки, а цвет —
тот, каким сейчас рисуется кнопка. Поэтому один значок годится и для
тёмной кнопки, и для оранжевой, и для недоступной. Перекрашенные
запоминаются: цветов ровно три.

Значок программы стал значком окна. Значок самого файла под Windows
задаётся не отсюда, а ресурсом при сборке — это осталось как было.

Окно «О программе» (Справка → О программе). Две вкладки: «Библиотеки и
авторство» и «Что умеет». Первая обязательна — бесплатная лицензия
Icons8 требует ссылку на каждый значок; заодно там перечислено всё, на
чём программа держится, и под какой лицензией. Списки разные по
системам: под Linux ffmpeg и libmpv ставит пакетный менеджер, под
Windows их скачивает сама программа, и это разные сборки.

Вкладки свои, а не TPageControl: системные вкладки под Windows остались
бы светлой полосой поверх тёмного окна.

Текст показывается нашим журналом — его можно выделить и скопировать,
так что ссылку из списка получится взять в браузер. Перенос по словам
свой: ссылки не рвутся, отступ списка сохраняется на продолжениях.

Свои рисованные значки из icons/ убраны: они больше не используются, и
держать рядом два набора значило бы путать.
This commit is contained in:
4aiman 2026-08-22 10:54:30 +03:00
commit 58d7748d51
17 changed files with 1025 additions and 122 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 482 B

View file

@ -1,11 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<!-- Журнал: полоса с сообщениями. Строки разной длины, как настоящий
текст: одинаковые читались бы как таблица. -->
<rect x="2" y="3" width="20" height="18" fill="none"
stroke="currentColor" stroke-width="2"/>
<g stroke="currentColor" stroke-width="2" stroke-linecap="square">
<line x1="6" y1="8" x2="18" y2="8"/>
<line x1="6" y1="12" x2="18" y2="12"/>
<line x1="6" y1="16" x2="13" y2="16"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

View file

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<!-- Схлопывание: дыра между кусками закрывается, правый кусок
переезжает влево. Пунктир — то место, которое исчезнет. -->
<rect x="1" y="7" width="7" height="10" fill="currentColor"/>
<rect x="16" y="7" width="7" height="10" fill="currentColor"/>
<line x1="9" y1="12" x2="15" y2="12" stroke="currentColor"
stroke-width="2" stroke-dasharray="2 2"/>
<polygon points="10,12 15,8 15,16" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 783 B

View file

@ -1,8 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<!-- Прилипание: подкова магнита. Полюса внизу закрашены сплошным —
так подкова не читается как перевёрнутая буква U. -->
<path d="M5 19 V13 a7 7 0 0 1 14 0 V19" fill="none"
stroke="currentColor" stroke-width="4"/>
<rect x="3" y="18" width="4" height="4" fill="currentColor"/>
<rect x="17" y="18" width="4" height="4" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 548 B

View file

@ -0,0 +1,4 @@
Log panel: <a target="_blank" href="https://icons8.com/icon/79872/property">Property</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
Ripple gaps: <a target="_blank" href="https://icons8.com/icon/2EQegp8tbR6Q/merge-vertical">Merge Vertical</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
Magnet: <a target="_blank" href="https://icons8.com/icon/79942/magnet">Magnet</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
App icon: <a target="_blank" href="https://icons8.com/icon/69093/showing-video-frames">Showing Video Frames</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

347
src/uAboutForm.pas Normal file
View file

@ -0,0 +1,347 @@
{
uAboutForm — окно «О программе».
Две вкладки. Первая обязательна: значки взяты у Icons8, а их бесплатная
лицензия требует указать авторство со ссылкой на каждый значок. Заодно
там же перечислено всё, на чём программа держится, и под какой лицензией
оно раздаётся — под Windows и под Linux наборы разные.
Вторая вкладка отвечает на вопрос «что эта штука умеет», который иначе
задают каждому новому человеку голосом.
Вкладки свои, а не TPageControl: системные вкладки рисует система, и под
Windows они остались бы светлой полосой поверх тёмного окна — ровно то,
из-за чего здесь уже переписаны кнопки, полосы прокрутки, журнал и меню.
Две кнопки-переключателя и две панели делают то же самое и выглядят как
остальная программа.
Текст показывается нашим журналом: он умеет прокрутку, выделение и
копирование в буфер — ссылку из списка можно взять и открыть в браузере.
}
unit uAboutForm;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Controls, Forms, Graphics, StdCtrls, Dialogs,
uTheme, uLogView;
{ Показывает окно. Возвращается, когда его закрыли. }
procedure ShowAboutForm;
implementation
{ Перенос по словам.
Журнал строки не переносит: он показывает сообщения программы, где
перенос сбил бы выравнивание. Здесь же текст сплошной, и без переноса
абзац уезжает за правый край.
Ссылки не трогаются вовсе: разорванную пополам ссылку не собрать
обратно. Отступ строки переносится на её продолжения — иначе список
рассыпается.
Ширина считается в знаках, а шрифт у нас пропорциональный, поэтому
строка выходит короче обещанного. Это лучше, чем длиннее: за правый край
не уедет ничего. }
function Wrap(const AText: string; AWidth: Integer): string;
var
Src: TStringList;
I, Space, Indent: Integer;
Line, Rest: string;
begin
Result := '';
Src := TStringList.Create;
try
Src.Text := AText;
for I := 0 to Src.Count - 1 do
begin
Rest := Src[I];
if (Rest = '') or (Length(Rest) <= AWidth) or (Pos('http', Rest) > 0) then
begin
Result := Result + Rest + LineEnding;
Continue;
end;
{ Отступ запоминается и повторяется на всех продолжениях. }
Indent := 0;
while (Indent < Length(Rest)) and (Rest[Indent + 1] = ' ') do
Inc(Indent);
while Length(Rest) > AWidth do
begin
{ Ищем последний пробел в пределах ширины; не нашли — рвём по
ширине, иначе одно длинное слово повесило бы разбор. }
Space := AWidth;
while (Space > Indent + 1) and (Rest[Space] <> ' ') do
Dec(Space);
if Space <= Indent + 1 then
Space := AWidth;
Line := Copy(Rest, 1, Space);
Rest := StringOfChar(' ', Indent) + Copy(Rest, Space + 1, Length(Rest));
Result := Result + TrimRight(Line) + LineEnding;
end;
Result := Result + Rest + LineEnding;
end;
finally
Src.Free;
end;
end;
type
{ Обработчики живут в объекте: событиям нужны методы. }
TAboutDialog = class(TComponent)
public
Form: TForm;
BtnCredits: TThemedButton;
BtnFeatures: TThemedButton;
ViewCredits: TLogView;
ViewFeatures: TLogView;
procedure ShowCredits(Sender: TObject);
procedure ShowFeatures(Sender: TObject);
procedure Select(ACredits: Boolean);
end;
procedure TAboutDialog.Select(ACredits: Boolean);
begin
BtnCredits.Down := ACredits;
BtnFeatures.Down := not ACredits;
ViewCredits.Visible := ACredits;
ViewFeatures.Visible := not ACredits;
end;
procedure TAboutDialog.ShowCredits(Sender: TObject);
begin
Select(True);
end;
procedure TAboutDialog.ShowFeatures(Sender: TObject);
begin
Select(False);
end;
{ Кто это написал и на чём оно держится.
Списки разные по системам не для красоты: под Linux ffmpeg и libmpv
ставит пакетный менеджер, под Windows их скачивает сама программа из
окна «Зависимости», и это разные сборки с разными лицензиями. Указывать
надо то, что стоит у человека, а не то, что бывает вообще. }
function CreditsText: string;
begin
Result :=
'nlve — монтаж без перекодирования' + LineEnding +
LineEnding +
'Автор: 4aiman' + LineEnding +
LineEnding +
'Замысел в одном: куски видео вырезаются и склеиваются копированием ' +
'потока, без пересчёта картинки. То, что обычный монтажный редактор ' +
'считает часами, здесь собирается за время копирования файла. ' +
'Пересобирается только то, что иначе никак: переходы, прозрачность, ' +
'точные резы и куски файлов с чужими параметрами.' + LineEnding +
LineEnding +
'── Значки ──' + LineEnding +
LineEnding +
'Значки взяты у Icons8 и требуют указания авторства.' + LineEnding +
LineEnding +
'Прилипание — значок «Magnet» от Icons8:' + LineEnding +
' https://icons8.com/icon/79942/magnet' + LineEnding +
'Схлопывание — значок «Merge Vertical» от Icons8:' + LineEnding +
' https://icons8.com/icon/2EQegp8tbR6Q/merge-vertical' + LineEnding +
'Журнал — значок «Property» от Icons8:' + LineEnding +
' https://icons8.com/icon/79872/property' + LineEnding +
'Значок программы — «Showing Video Frames» от Icons8:' + LineEnding +
' https://icons8.com/icon/69093/showing-video-frames' + LineEnding +
LineEnding +
'Сам Icons8: https://icons8.com' + LineEnding +
LineEnding +
'── Чем режется и что показывает ──' + LineEnding +
LineEnding +
{$IFDEF WINDOWS}
'ffmpeg и ffprobe — вся работа с видео и звуком: нарезка, склейка, ' +
'перекодирование, разбор файлов.' + LineEnding +
' Сборка BtbN, вариант gpl:' + LineEnding +
' https://github.com/BtbN/FFmpeg-Builds' + LineEnding +
' Лицензия сборки: GPL-3.0' + LineEnding +
' Сам ffmpeg: https://ffmpeg.org' + LineEnding +
LineEnding +
'libmpv — предпросмотр монтажа. Играет виртуальный список кусков, ' +
'не собирая при этом ни одного файла.' + LineEnding +
' Сборка shinchiro/mpv-winbuild-cmake:' + LineEnding +
' https://github.com/shinchiro/mpv-winbuild-cmake' + LineEnding +
' Лицензия сборки: GPL' + LineEnding +
' Сам mpv: https://mpv.io' + LineEnding +
LineEnding +
'7-Zip (7zr.exe) — распаковка сборок libmpv, они раздаются в .7z.' +
LineEnding +
' https://www.7-zip.org' + LineEnding +
' Лицензия: LGPL-2.1' + LineEnding +
{$ELSE}
'ffmpeg и ffprobe — вся работа с видео и звуком: нарезка, склейка, ' +
'перекодирование, разбор файлов.' + LineEnding +
' Ставится пакетным менеджером: sudo apt install ffmpeg' + LineEnding +
' https://ffmpeg.org' + LineEnding +
' Лицензия сборок в Ubuntu: GPL-2 и новее' + LineEnding +
LineEnding +
'libmpv — предпросмотр монтажа. Играет виртуальный список кусков, ' +
'не собирая при этом ни одного файла.' + LineEnding +
' Ставится пакетным менеджером: sudo apt install mpv' + LineEnding +
' https://mpv.io' + LineEnding +
' Лицензия сборок в Ubuntu: GPL-3 и новее' + LineEnding +
{$ENDIF}
LineEnding +
'── На чём написано ──' + LineEnding +
LineEnding +
'Free Pascal — компилятор.' + LineEnding +
' https://www.freepascal.org' + LineEnding +
' Лицензия: GPL с оговоркой о компоновке для библиотеки времени ' +
'выполнения' + LineEnding +
LineEnding +
'Lazarus LCL — библиотека окон и элементов управления.' + LineEnding +
' https://www.lazarus-ide.org' + LineEnding +
' Лицензия: LGPL-2 с оговоркой о компоновке' + LineEnding +
LineEnding +
'Ни ffmpeg, ни libmpv в состав программы не входят и вместе с ней не ' +
'раздаются: ffmpeg запускается отдельной программой, libmpv ' +
'подключается на ходу и ставится отдельно.';
end;
{ Что программа умеет. Не список кнопок, а список того, ради чего её
открывают. }
function FeaturesText: string;
begin
Result :=
'Монтаж' + LineEnding +
LineEnding +
' Дорожки для картинки и для звука, сколько нужно. При перекрытии ' +
'картинку даёт верхняя дорожка целиком, а звук всех дорожек ' +
'смешивается: под голос кладётся музыка.' + LineEnding +
' Клипы переносятся, растягиваются за края, режутся по курсору ' +
'(S) и удаляются. Прилипание к краям соседей и к курсору; Shift во ' +
'время перетаскивания отменяет его.' + LineEnding +
' Связки: видео и его звук ходят вместе, Ctrl+G связывает и ' +
'развязывает. Развязанный звук двигается отдельно от своей картинки.' +
LineEnding +
' Схлопывание дыр: вручную (Ctrl+Shift+R) или само при ' +
'перетаскивании, на текущей дорожке или на всех сразу.' + LineEnding +
' Отмена и повтор любого действия.' + LineEnding +
LineEnding +
'Что видно на дорожках' + LineEnding +
LineEnding +
' Форма звука на звуковых клипах; Shift со стрелками вверх и вниз ' +
'усиливает её, когда запись тихая.' + LineEnding +
' Громкость клипа — ручкой прямо на нём, прозрачность — так же на ' +
'видеоклипе.' + LineEnding +
' Появление и уход — за верхние углы клипа. Перекрытие двух клипов ' +
'одной дорожки помечено косым крестом: там сам собой выходит переход.' +
LineEnding +
' Цвет краёв показывает, притянут рез к опорному кадру или потребует ' +
'перекодирования.' + LineEnding +
LineEnding +
'Предпросмотр' + LineEnding +
LineEnding +
' Играет монтаж целиком, не собирая ни одного файла: плееру ' +
'скармливается виртуальный список кусков.' + LineEnding +
' Показывает кадр проекта, а не кадр очередного исходника — что ' +
'видно здесь, то и получится в ролике.' + LineEnding +
' Участки с прозрачностью и переходами заранее сводятся в отдельные ' +
'файлы, поэтому видны и они.' + LineEnding +
LineEnding +
'Кадрирование' + LineEnding +
LineEnding +
' У каждого клипа своё: вписать целиком, заполнить кадр с обрезкой ' +
'или растянуть. Плюс увеличение, смещение, поворот и отражение.' +
LineEnding +
' Рамку в окне кадрирования можно тянуть мышью, а не только задавать ' +
'числами.' + LineEnding +
LineEnding +
'Сборка ролика' + LineEnding +
LineEnding +
' Обычная — копированием потоков. Час материала собирается за время ' +
'копирования файла.' + LineEnding +
' Перед сборкой программа говорит, сколько секунд придётся ' +
'пересобрать и почему.' + LineEnding +
' Точный рез перекодирует только огрызок до ближайшего опорного ' +
'кадра, остальное копируется.' + LineEnding +
' Отдельно — сборка с перекодированием: размер кадра от 720p до 4K, ' +
'поток в килобитах, кодек HEVC или H.264. Считает видеокарта.' +
LineEnding +
' Перепаковка готовых файлов в другой контейнер, без пересчёта.' +
LineEnding +
LineEnding +
'Мелочи, которые важны' + LineEnding +
LineEnding +
' Индекс опорных кадров читается из заголовка файла, а не ' +
'перебором: на шестигигабайтном файле это двадцать миллисекунд ' +
'вместо двенадцати секунд.' + LineEnding +
' Фоновая работа видна внизу полоской и отмечена на монтаже ' +
'штриховкой.' + LineEnding +
' Все окна в своих цветах, а не в системных.' + LineEnding +
' Настройки, недавние проекты и размеры окна переживают перезапуск.';
end;
procedure ShowAboutForm;
var
Dlg: TAboutDialog;
BtnClose: TThemedButton;
begin
Dlg := TAboutDialog.Create(nil);
try
Dlg.Form := TForm.CreateNew(nil);
Dlg.Form.Caption := 'О программе';
Dlg.Form.Position := poScreenCenter;
Dlg.Form.ClientWidth := 720;
Dlg.Form.ClientHeight := 560;
Dlg.Form.Constraints.MinWidth := 480;
Dlg.Form.Constraints.MinHeight := 320;
Dlg.BtnCredits := TThemedButton.Create(Dlg.Form);
Dlg.BtnCredits.Parent := Dlg.Form;
Dlg.BtnCredits.SetBounds(12, 12, 240, 28);
Dlg.BtnCredits.Caption := 'Библиотеки и авторство';
Dlg.BtnCredits.TabStop := False;
Dlg.BtnCredits.OnClick := @Dlg.ShowCredits;
Dlg.BtnFeatures := TThemedButton.Create(Dlg.Form);
Dlg.BtnFeatures.Parent := Dlg.Form;
Dlg.BtnFeatures.SetBounds(258, 12, 180, 28);
Dlg.BtnFeatures.Caption := 'Что умеет';
Dlg.BtnFeatures.TabStop := False;
Dlg.BtnFeatures.OnClick := @Dlg.ShowFeatures;
Dlg.ViewCredits := TLogView.Create(Dlg.Form);
Dlg.ViewCredits.Parent := Dlg.Form;
Dlg.ViewCredits.SetBounds(12, 48, 696, 460);
Dlg.ViewCredits.Anchors := [akTop, akLeft, akRight, akBottom];
{ Ширина подобрана замером: при 720 точках окна в строку входит около
ста шестидесяти знаков, берём с запасом. }
Dlg.ViewCredits.SetText(Wrap(CreditsText, 150));
Dlg.ViewFeatures := TLogView.Create(Dlg.Form);
Dlg.ViewFeatures.Parent := Dlg.Form;
Dlg.ViewFeatures.SetBounds(12, 48, 696, 460);
Dlg.ViewFeatures.Anchors := [akTop, akLeft, akRight, akBottom];
Dlg.ViewFeatures.SetText(Wrap(FeaturesText, 150));
BtnClose := TThemedButton.Create(Dlg.Form);
BtnClose.Parent := Dlg.Form;
BtnClose.SetBounds(608, 520, 100, 28);
BtnClose.Anchors := [akRight, akBottom];
BtnClose.Caption := 'Закрыть';
BtnClose.ModalResult := mrOk;
BtnClose.Default := True;
BtnClose.Cancel := True;
ApplyDarkTheme(Dlg.Form);
{ Значок окна берётся у главного окна: своего у диалога нет. }
Dlg.Select(True);
Dlg.Form.ShowModal;
finally
Dlg.Form.Free;
Dlg.Free;
end;
end;
end.

398
src/uIconData.pas Normal file
View file

@ -0,0 +1,398 @@
{
uIconData — значки, вшитые в программу.
Собран из файлов в icons_online командой:
python3 tools/make_icons.py
Правится не он, а исходные картинки: этот файл переписывается целиком.
Значки взяты у Icons8 и требуют указания авторства — оно есть в окне
«О программе», вкладка «Библиотеки и авторство». Ссылки на каждый значок
лежат в icons_online/attribution.txt.
Картинки чёрные на прозрачном фоне. Цвет им задаётся при отрисовке:
берётся прозрачность точки, а цвет — тот, каким сейчас рисуется кнопка.
Поэтому один и тот же значок годится и для тёмной кнопки, и для
оранжевой.
}
unit uIconData;
{$mode objfpc}{$H+}
interface
const
{ icons8-магнит-30.png, 24 на 24 точек }
IconSnapData: array[0..472] of Byte = (
$89, $50, $4E, $47, $0D, $0A, $1A, $0A, $00, $00, $00, $0D, $49, $48, $44, $52,
$00, $00, $00, $18, $00, $00, $00, $18, $08, $04, $00, $00, $00, $4A, $7E, $F5,
$73, $00, $00, $00, $20, $63, $48, $52, $4D, $00, $00, $7A, $26, $00, $00, $80,
$84, $00, $00, $FA, $00, $00, $00, $80, $E8, $00, $00, $75, $30, $00, $00, $EA,
$60, $00, $00, $3A, $98, $00, $00, $17, $70, $9C, $BA, $51, $3C, $00, $00, $00,
$02, $62, $4B, $47, $44, $00, $00, $AA, $8D, $23, $32, $00, $00, $00, $09, $70,
$48, $59, $73, $00, $00, $0B, $13, $00, $00, $0B, $13, $01, $00, $9A, $9C, $18,
$00, $00, $00, $07, $74, $49, $4D, $45, $07, $EA, $08, $16, $07, $2D, $16, $EF,
$B8, $28, $FD, $00, $00, $01, $3E, $49, $44, $41, $54, $38, $CB, $BD, $92, $31,
$4B, $C3, $40, $14, $C7, $7F, $97, $44, $29, $68, $03, $5A, $45, $EA, $D0, $A5,
$6B, $41, $44, $5C, $0A, $0E, $3A, $15, $DC, $1D, $DD, $FB, $51, $74, $71, $29,
$F8, $39, $EC, $62, $06, $17, $5D, $EA, $22, $D2, $88, $93, $E0, $22, $68, $A1,
$AD, $14, $AA, $A0, $B6, $E6, $39, $24, $17, $AE, $69, $32, $88, $D4, $77, $4B,
$EE, $DD, $EF, $7F, $EF, $DD, $FB, $07, $66, $1D, $6A, $6A, $EF, $B2, $4D, $85,
$22, $F3, $74, $B9, $E3, $9A, $1E, $92, $2D, $5F, $E0, $80, $0B, $9E, $78, $27,
$40, $F8, $E4, $85, $4B, $0E, $71, $B3, $F0, $35, $4E, $78, $45, $12, $6B, $40,
$83, $62, $1A, $BE, $44, $83, $AF, $29, $5C, $10, $46, $9C, $B2, $9C, $C4, $1D,
$EA, $0C, $53, $71, $41, $78, $A3, $8E, $33, $29, $28, $E1, $67, $E2, $82, $E0,
$53, $0A, $41, $2B, $9A, $CD, $1E, $65, $43, $3E, $C0, $C7, $67, $60, $64, $CA,
$EC, $86, $13, $D5, $82, $2A, $B9, $F8, $B0, $C7, $11, $35, $6A, $1C, $D3, $8F,
$73, $39, $AA, $A6, $05, $16, $9E, $51, $BE, $49, $01, $80, $02, $4D, $23, $EB,
$85, $97, $EB, $0A, $E6, $AC, $3B, $D1, $CD, $7D, $3A, $46, $D6, $35, $5B, $9A,
$74, $5C, $52, $BE, $62, $C2, $E2, $97, $31, $7B, $81, $F6, $4F, $F1, $41, $1B,
$61, $8E, $AD, $04, $71, $C3, $08, $C5, $86, $7E, $83, $16, $D8, $DC, $B3, $C3,
$37, $AB, $3C, $18, $03, $50, $0C, $D9, $A7, $8B, $4D, $0B, $FB, $8F, $2D, $8D,
$D9, $A4, $85, $E0, $90, $27, $88, $4F, $03, $F2, $78, $8C, $51, $54, $B8, $D5,
$AD, $84, $A5, $D7, $59, $C4, $02, $84, $47, $CE, $68, $47, $82, $02, $2B, $11,
$F1, $CC, $39, $57, $88, $B6, $43, $A1, $8C, $CE, $83, $D8, $30, $65, $B4, $1C,
$FE, $20, $FF, $10, $3F, $90, $74, $7D, $D8, $DF, $AB, $6B, $DD, $00, $00, $00,
$00, $49, $45, $4E, $44, $AE, $42, $60, $82
);
{ icons8-merge-vertical-64.png, 24 на 24 точек }
IconRippleData: array[0..457] of Byte = (
$89, $50, $4E, $47, $0D, $0A, $1A, $0A, $00, $00, $00, $0D, $49, $48, $44, $52,
$00, $00, $00, $18, $00, $00, $00, $18, $08, $04, $00, $00, $00, $4A, $7E, $F5,
$73, $00, $00, $00, $20, $63, $48, $52, $4D, $00, $00, $7A, $26, $00, $00, $80,
$84, $00, $00, $FA, $00, $00, $00, $80, $E8, $00, $00, $75, $30, $00, $00, $EA,
$60, $00, $00, $3A, $98, $00, $00, $17, $70, $9C, $BA, $51, $3C, $00, $00, $00,
$02, $62, $4B, $47, $44, $00, $00, $AA, $8D, $23, $32, $00, $00, $00, $09, $70,
$48, $59, $73, $00, $00, $0B, $13, $00, $00, $0B, $13, $01, $00, $9A, $9C, $18,
$00, $00, $00, $07, $74, $49, $4D, $45, $07, $EA, $08, $16, $07, $2D, $16, $EF,
$B8, $28, $FD, $00, $00, $01, $2F, $49, $44, $41, $54, $38, $CB, $CD, $D2, $3D,
$2F, $83, $51, $18, $C6, $F1, $DF, $C3, $63, $31, $34, $11, $B5, $35, $D1, $A9,
$C1, $E0, $25, $4C, $66, $49, $F9, $16, $24, $96, $C6, $37, $A8, $18, $7D, $02,
$31, $98, $18, $99, $25, $44, $F8, $02, $26, $B1, $30, $30, $78, $D9, $90, $18,
$24, $35, $F4, $B4, $8F, $A1, $8A, $7A, $5A, $D2, $45, $FC, $C7, $73, $DF, $D7,
$7D, $AE, $EB, $3E, $87, $2E, $89, $52, $27, $3D, $7A, $50, $43, $2F, $EA, $EA,
$BF, $8D, $28, $3A, $B6, $A9, $5F, $BF, $4D, $C7, $8A, $DF, $CB, $71, $4A, $90,
$33, $27, $2B, $C6, $AC, $49, $BB, $69, $03, $5D, $F2, $77, $82, $A8, $CD, $BE,
$BE, $F2, $51, $6F, $0A, $46, $2D, $E8, $EB, $D8, $1E, $2B, $1A, $69, $15, $0C,
$DB, $B6, $66, $A8, $6D, $7B, $D6, $AA, $1D, $F9, $A6, $B6, $60, $40, $50, $90,
$55, $36, $A3, $FC, $CD, $5A, $64, $C2, $BA, $79, $91, $82, $07, $B1, $67, $F6,
$54, $BC, $78, $95, $48, $24, $AE, $1D, $49, $9C, $C9, $C8, $38, $93, $38, $74,
$F5, $5E, $79, $F5, $A2, $62, $2F, $16, $54, $D5, $BE, $CC, $4C, $3A, $E4, $08,
$AA, $EA, $02, $79, $E3, $C6, $94, $D4, $04, $07, $26, $2D, $B7, $DC, $B0, $6C,
$C2, $BE, $A0, $A6, $64, $CC, $B8, $7C, $EC, $E6, $3D, $F4, $93, $2D, $1B, $1E,
$4D, $B7, $CC, $4D, $9C, $5B, $B2, $A2, $E4, $C6, $45, $23, $74, $83, $5B, $8B,
$4E, $54, $DB, $9A, $79, $B2, $EE, $D4, $5D, $73, $4B, $0D, $2E, $5D, $76, $74,
$4F, $70, $F4, $F9, $24, $3F, $47, $4D, $AD, $E2, $1F, $FF, $D6, $4F, $AA, $2A,
$EE, $05, $C1, $BD, $4A, $87, $BD, $B5, $30, $68, $4A, $4E, $24, $92, $33, $65,
$B0, $5B, $07, $29, $DE, $00, $F6, $77, $56, $B3, $E2, $94, $4B, $80, $00, $00,
$00, $00, $49, $45, $4E, $44, $AE, $42, $60, $82
);
{ icons8-property-30.png, 24 на 24 точек }
IconLogData: array[0..425] of Byte = (
$89, $50, $4E, $47, $0D, $0A, $1A, $0A, $00, $00, $00, $0D, $49, $48, $44, $52,
$00, $00, $00, $18, $00, $00, $00, $18, $08, $04, $00, $00, $00, $4A, $7E, $F5,
$73, $00, $00, $00, $20, $63, $48, $52, $4D, $00, $00, $7A, $26, $00, $00, $80,
$84, $00, $00, $FA, $00, $00, $00, $80, $E8, $00, $00, $75, $30, $00, $00, $EA,
$60, $00, $00, $3A, $98, $00, $00, $17, $70, $9C, $BA, $51, $3C, $00, $00, $00,
$02, $62, $4B, $47, $44, $00, $00, $AA, $8D, $23, $32, $00, $00, $00, $09, $70,
$48, $59, $73, $00, $00, $0B, $13, $00, $00, $0B, $13, $01, $00, $9A, $9C, $18,
$00, $00, $00, $07, $74, $49, $4D, $45, $07, $EA, $08, $16, $07, $2D, $16, $EF,
$B8, $28, $FD, $00, $00, $01, $0F, $49, $44, $41, $54, $38, $CB, $B5, $D3, $BF,
$2E, $83, $51, $18, $C7, $F1, $CF, $5B, $15, $0C, $74, $93, $88, $91, $0B, $E8,
$68, $60, $33, $34, $B1, $1A, $F4, $1E, $0C, $66, $8B, $C5, $05, $B8, $01, $89,
$2B, $30, $18, $24, $4C, $0D, $8B, $41, $13, $46, $62, $90, $18, $A4, $4B, $55,
$3A, $28, $AD, $F7, $18, $AA, $F5, $36, $DA, $B7, $25, $3C, $67, $38, $27, $4F,
$9E, $EF, $79, $FE, $9C, $F3, $E3, $BF, $2D, $42, $24, $12, $8D, $10, $1B, $04,
$21, $8B, $39, $5B, $96, $65, $84, $D4, $8B, $63, $E7, $F6, $3C, $42, $51, $A3,
$4D, $0F, $59, $2F, $36, $C8, $60, $C6, $C4, $48, $E5, $4F, $CA, $B5, $81, $1F,
$59, $F6, $73, $7F, $D6, $1C, $12, $39, $2E, $97, $04, $8A, $2E, $BA, $E7, $7E,
$D6, $B2, $E4, $38, $09, $D4, $3C, $0D, $C9, $50, $EB, $2D, $09, $F2, $0A, $0E,
$B0, $69, $36, $E1, $6D, $3A, $72, $F2, $BD, $07, $98, $B7, $EA, $50, $B0, $66,
$B1, $FB, $26, $91, $86, $DB, $41, $C0, $A9, $92, $86, $60, $A5, $67, $76, $C1,
$5B, $BF, $29, $B5, $33, $E4, $95, $50, $90, $4B, $64, $68, $B9, $74, $DD, $1F,
$C8, $DB, $76, $23, $D8, $B5, $90, $F0, $BE, $DA, $19, $04, $9C, $B9, $73, $8F,
$75, $53, $09, $6F, $EC, $61, $50, $49, $55, $55, $70, $95, $36, $DD, $5F, $7F,
$8D, $08, $63, $29, $71, $EF, $1D, $C5, $74, $80, $7D, $F5, $54, $11, $05, $D3,
$5F, $40, $45, $59, $3C, $44, $73, $91, $BA, $B2, $4A, $47, $A2, $A3, $76, $12,
$A7, $AA, $F2, $8F, $EC, $03, $0F, $C8, $51, $5A, $EC, $EA, $63, $7C, $00, $00,
$00, $00, $49, $45, $4E, $44, $AE, $42, $60, $82
);
{ icons8-показ-видеокадров-30.png, значок окна, 32 на 32 точек, формат ico }
IconAppData: array[0..4285] of Byte = (
$00, $00, $01, $00, $01, $00, $20, $20, $00, $00, $01, $00, $20, $00, $A8, $10,
$00, $00, $16, $00, $00, $00, $28, $00, $00, $00, $20, $00, $00, $00, $40, $00,
$00, $00, $01, $00, $20, $00, $00, $00, $00, $00, $00, $10, $00, $00, $13, $0B,
$00, $00, $13, $0B, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $37, $00, $00, $00, $A1, $00, $00, $00, $BB, $00, $00,
$00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00,
$00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00,
$00, $BB, $00, $00, $00, $A1, $00, $00, $00, $37, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $51, $00, $00, $00, $E5, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $E5, $00, $00, $00, $51, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $1E, $00, $00, $00, $58, $00, $00, $00, $66, $00, $00,
$00, $66, $00, $00, $00, $66, $00, $00, $00, $66, $00, $00, $00, $66, $00, $00,
$00, $66, $00, $00, $00, $66, $00, $00, $00, $66, $00, $00, $00, $66, $00, $00,
$00, $66, $00, $00, $00, $58, $00, $00, $00, $1E, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $15, $00, $00,
$00, $54, $00, $00, $00, $74, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00,
$00, $77, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00,
$00, $77, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00,
$00, $77, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00,
$00, $77, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00, $00, $77, $00, $00,
$00, $77, $00, $00, $00, $77, $00, $00, $00, $74, $00, $00, $00, $54, $00, $00,
$00, $15, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $69, $00, $00,
$00, $DB, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $DA, $00, $00,
$00, $68, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $BE, $00, $00,
$00, $FF, $00, $00, $00, $C7, $00, $00, $00, $A4, $00, $00, $00, $D7, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $D7, $00, $00, $00, $A4, $00, $00, $00, $C7, $00, $00, $00, $FF, $00, $00,
$00, $BD, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $55, $00, $00, $00, $00, $00, $00, $00, $88, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $88, $00, $00, $00, $00, $00, $00, $00, $55, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $79, $00, $00, $00, $29, $00, $00, $00, $A1, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $A1, $00, $00, $00, $29, $00, $00, $00, $79, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $EC, $00, $00, $00, $E1, $00, $00, $00, $F2, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $F2, $00, $00, $00, $E1, $00, $00, $00, $EC, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $5C, $00, $00, $00, $00, $00, $00, $00, $8D, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $8D, $00, $00, $00, $00, $00, $00, $00, $5C, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $5C, $00, $00, $00, $00, $00, $00, $00, $8D, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $8D, $00, $00, $00, $00, $00, $00, $00, $5C, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $EC, $00, $00, $00, $E1, $00, $00, $00, $F2, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $F2, $00, $00, $00, $E1, $00, $00, $00, $EC, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $79, $00, $00, $00, $29, $00, $00, $00, $A1, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $A1, $00, $00, $00, $29, $00, $00, $00, $79, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $55, $00, $00, $00, $00, $00, $00, $00, $88, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $88, $00, $00, $00, $00, $00, $00, $00, $55, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $C6, $00, $00, $00, $A4, $00, $00, $00, $D7, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $D7, $00, $00, $00, $A4, $00, $00, $00, $C6, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $A4, $00, $00, $00, $6E, $00, $00, $00, $C0, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $C0, $00, $00, $00, $6E, $00, $00, $00, $A4, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C9, $00, $00,
$00, $FF, $00, $00, $00, $55, $00, $00, $00, $00, $00, $00, $00, $88, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $88, $00, $00, $00, $00, $00, $00, $00, $55, $00, $00, $00, $FF, $00, $00,
$00, $C9, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $C3, $00, $00,
$00, $FF, $00, $00, $00, $99, $00, $00, $00, $5C, $00, $00, $00, $B8, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $B8, $00, $00, $00, $5C, $00, $00, $00, $99, $00, $00, $00, $FF, $00, $00,
$00, $C3, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $8A, $00, $00,
$00, $EA, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00,
$00, $FF, $00, $00, $00, $FF, $00, $00, $00, $FF, $00, $00, $00, $EA, $00, $00,
$00, $89, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $26, $00, $00,
$00, $87, $00, $00, $00, $B6, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00,
$00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00,
$00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00,
$00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00,
$00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00, $00, $BA, $00, $00,
$00, $BA, $00, $00, $00, $BA, $00, $00, $00, $B6, $00, $00, $00, $86, $00, $00,
$00, $24, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $FF, $FF,
$FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $80,
$01, $FF, $FF, $80, $01, $FF, $FF, $80, $01, $FF, $FF, $FF, $FF, $FF, $E0, $00,
$00, $07, $E0, $00, $00, $07, $E0, $00, $00, $07, $E2, $00, $00, $47, $E0, $00,
$00, $07, $E0, $00, $00, $07, $E0, $00, $00, $07, $E2, $00, $00, $47, $E2, $00,
$00, $47, $E0, $00, $00, $07, $E0, $00, $00, $07, $E0, $00, $00, $07, $E2, $00,
$00, $47, $E0, $00, $00, $07, $E0, $00, $00, $07, $E0, $00, $00, $07, $E2, $00,
$00, $47, $E0, $00, $00, $07, $E0, $00, $00, $07, $E0, $00, $00, $07, $FF, $FF,
$FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF
);
implementation
end.

View file

@ -25,10 +25,11 @@ uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
ComCtrls, Menus, Buttons, LCLType, LMessages,
uAppTypes, uEdl, uMpvLib, uMpvPlayer, uProject, uMediaProbe, uTimelineCtrl,
uFlatten, uGenerate, uExport, uEncodeForm, uProgressForm, uRemuxForm,
uMismatchForm,
uFlatten, uGenerate, uExport, uEncodeForm, uAboutForm, uProgressForm,
uRemuxForm, uMismatchForm,
uFfmpegJob, uSettings, uThumbs, uTransform, uPanCropForm,
uProjectInfoForm, ImgList, uTheme, uLogView, uDepsForm, uWaveform;
uProjectInfoForm, ImgList, uTheme, uLogView, uDepsForm, uWaveform,
uIconData;
const
{ Номера системных сообщений о перерисовке рамки окна. Берём числами, а
@ -207,11 +208,13 @@ type
{ --- окно и меню --- }
procedure BuildMenu;
procedure ApplyWindowIcon;
procedure BuildJobIndicator;
procedure JobTimerTick(Sender: TObject);
procedure AdvanceJobBar;
procedure LogButtonClick(Sender: TObject);
procedure MenuToggleLog(Sender: TObject);
procedure MenuAbout(Sender: TObject);
procedure ApplyLogVisible(AVisible: Boolean);
procedure BuildTimeline;
procedure StartPlayer;
@ -566,7 +569,7 @@ procedure TMainForm.BuildMenu;
end;
var
MFile, MEdit, MView, MProject: TMenuItem;
MFile, MEdit, MView, MProject, MHelp: TMenuItem;
begin
{ Подсказки всплывают почти сразу: на кнопке со значком они не
украшение, а единственный способ узнать, что она делает. Держатся
@ -575,6 +578,8 @@ begin
Application.HintPause := 100;
Application.HintHidePause := 15000;
ApplyWindowIcon;
BuildJobIndicator;
FMenu := TMainMenu.Create(Self);
@ -635,6 +640,9 @@ begin
AddItem(MProject, 'Добавить дорожку', @MenuAddTrack);
AddItem(MProject, 'Удалить последнюю дорожку', @MenuRemoveTrack);
MHelp := AddTop('Справка');
AddItem(MHelp, 'О программе...', @MenuAbout);
{ Дважды, и оба раза нужны.
До присваивания — чтобы обработчики отрисовки стояли к первому показу.
@ -656,6 +664,28 @@ end;
Место справа в нижней полосе отводится сразу и насовсем, а надпись с
полоской показываются только когда работа есть. Так строка состояния и
показатель не наезжают друг на друга. }
{ Значок окна. Вшит в программу, потому что рядом с ней может ничего не
лежать. Значок самого файла программы под Windows задаётся не здесь, а
ресурсом при сборке — это отдельная история. }
procedure TMainForm.ApplyWindowIcon;
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Stream.WriteBuffer(IconAppData[0], Length(IconAppData));
Stream.Position := 0;
try
Icon.LoadFromStream(Stream);
Application.Icon.Assign(Icon);
except
{ Со значком не вышло — работать это не мешает. }
end;
finally
Stream.Free;
end;
end;
procedure TMainForm.BuildJobIndicator;
const
JobWidth = 330;
@ -724,6 +754,11 @@ begin
ApplyLogVisible(not memoLog.Visible);
end;
procedure TMainForm.MenuAbout(Sender: TObject);
begin
ShowAboutForm;
end;
procedure TMainForm.MenuToggleLog(Sender: TObject);
begin
ApplyLogVisible(not memoLog.Visible);

View file

@ -31,7 +31,8 @@ interface
uses
Classes, SysUtils, Types, Controls, Graphics, Forms, StdCtrls, ExtCtrls,
ComCtrls, Grids, Menus, Dialogs, Math, LCLType, LCLIntf, LCLProc;
ComCtrls, Grids, Menus, Dialogs, Math, IntfGraphics, FPimage,
LCLType, LCLIntf, LCLProc, uIconData;
const
{ Порядок байт в TColor — $00BBGGRR, а не привычный RGB. У серых цветов
@ -55,10 +56,13 @@ const
type
{ Значок на кнопке.
Рисуется кодом, а не берётся из файла: программа не должна зависеть
от того, лежит ли рядом с ней картинка. Рисунки повторяют icons/*.svg
— там они и придуманы, оттуда их можно перегнать в png для чего
угодно ещё. }
Картинки вшиты в программу (uIconData): зависеть от того, лежит ли
рядом с ней файл, она не должна. Исходники — в icons_online, оттуда
же требование указать авторство, см. окно «О программе».
Цвет значку задаётся при отрисовке: берётся прозрачность точки, а
цвет — тот, каким сейчас рисуется кнопка. Поэтому один значок годится
и для тёмной кнопки, и для оранжевой, и для недоступной. }
TThemedIcon = (
tiNone,
tiSnap, // прилипание: края притягиваются к общей линии
@ -296,6 +300,8 @@ type
var
Painter: TThemePainter;
{ Перекрашенные значки: ключ «номер значка : цвет». }
IconCache: TStringList;
{$IFDEF WINDOWS}
{ Кисть под полосу меню. Создаётся один раз: Windows держит на неё
ссылку, пока меню живо, и удалять её раньше нельзя. }
@ -383,96 +389,101 @@ begin
Invalidate;
end;
{ Значки рисуются по клетке 24 на 24 — той же, что в icons/*.svg, — и
переносятся в заданный прямоугольник. Так рисунок один и тот же при
любом размере кнопки, и правится он в одном месте. }
{ Значок в нужном цвете.
Картинка чёрная с прозрачностью, поэтому перекрашивается прямо по
точкам: прозрачность остаётся своя, цвет ставится наш. Результат
запоминается — цветов ровно три (обычный, на оранжевом, недоступный), и
пересчитывать их на каждую перерисовку незачем. }
function ThemedIconImage(AIcon: TThemedIcon; AColor: TColor):
TPortableNetworkGraphic;
var
Key: string;
Idx, X, Y: Integer;
Stream: TMemoryStream;
Img: TLazIntfImage;
Px: TFPColor;
R, G, B: Byte;
begin
Result := nil;
if AIcon = tiNone then
Exit;
Key := IntToStr(Ord(AIcon)) + ':' + IntToStr(AColor);
if IconCache = nil then
begin
IconCache := TStringList.Create;
IconCache.Sorted := True;
IconCache.OwnsObjects := True;
end;
Idx := IconCache.IndexOf(Key);
if Idx >= 0 then
Exit(TPortableNetworkGraphic(IconCache.Objects[Idx]));
Stream := TMemoryStream.Create;
try
case AIcon of
tiSnap: Stream.WriteBuffer(IconSnapData[0], Length(IconSnapData));
tiRipple: Stream.WriteBuffer(IconRippleData[0], Length(IconRippleData));
tiLog: Stream.WriteBuffer(IconLogData[0], Length(IconLogData));
else
Exit;
end;
Stream.Position := 0;
Result := TPortableNetworkGraphic.Create;
try
Result.LoadFromStream(Stream);
{ Порядок байт в TColor обратный привычному. }
R := AColor and $FF;
G := (AColor shr 8) and $FF;
B := (AColor shr 16) and $FF;
Img := Result.CreateIntfImage;
try
for Y := 0 to Img.Height - 1 do
for X := 0 to Img.Width - 1 do
begin
Px := Img.Colors[X, Y];
{ Прозрачное трогать незачем, а видимое красим целиком. }
if Px.Alpha = 0 then
Continue;
Px.Red := R * 257;
Px.Green := G * 257;
Px.Blue := B * 257;
Img.Colors[X, Y] := Px;
end;
Result.LoadFromIntfImage(Img);
finally
Img.Free;
end;
except
FreeAndNil(Result);
end;
finally
Stream.Free;
end;
{ Неудачу тоже запоминаем: иначе разбирать картинку заново пришлось бы
на каждой перерисовке. }
IconCache.AddObject(Key, Result);
end;
{ Значок рисуется без растягивания: картинка вшита ровно того размера, под
который сделана кнопка. Если кнопка оказалась крупнее — значок остаётся
своего размера и стоит по центру, а не расплывается. }
procedure TThemedButton.DrawIcon(const ARect: TRect; AColor: TColor);
var
Side, X0, Y0: Integer;
function PX(AValue: Integer): Integer;
begin
Result := X0 + Round(AValue * Side / 24);
end;
function PY(AValue: Integer): Integer;
begin
Result := Y0 + Round(AValue * Side / 24);
end;
procedure Line(X1, Y1, X2, Y2: Integer);
begin
Canvas.Line(PX(X1), PY(Y1), PX(X2), PY(Y2));
end;
procedure Box(X1, Y1, X2, Y2: Integer; AFilled: Boolean);
begin
if AFilled then
Canvas.Brush.Style := bsSolid
else
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(PX(X1), PY(Y1), PX(X2), PY(Y2));
end;
{ Треугольник остриём влево или вправо. Не Arrow: так называется
свойство самой кнопки. }
procedure Tri(ATipX, ABaseX, ACenterY, AHalf: Integer);
begin
Canvas.Brush.Style := bsSolid;
Canvas.Polygon([Types.Point(PX(ATipX), PY(ACenterY)),
Types.Point(PX(ABaseX), PY(ACenterY - AHalf)),
Types.Point(PX(ABaseX), PY(ACenterY + AHalf))]);
end;
Img: TPortableNetworkGraphic;
X, Y: Integer;
begin
if FIcon = tiNone then
Img := ThemedIconImage(FIcon, AColor);
if (Img = nil) or (Img.Width = 0) then
Exit;
Side := Min(ARect.Right - ARect.Left, ARect.Bottom - ARect.Top) - 6;
if Side < 8 then
Exit;
X0 := (ARect.Left + ARect.Right - Side) div 2;
Y0 := (ARect.Top + ARect.Bottom - Side) div 2;
Canvas.Pen.Color := AColor;
Canvas.Pen.Width := Max(1, Side div 12);
Canvas.Brush.Color := AColor;
case FIcon of
tiSnap:
begin
{ Подкова магнита: дуга сверху, две ноги вниз, полюса закрашены.
Без закрашенных полюсов подкова читается как перевёрнутая
буква U и смысла не передаёт. }
Canvas.Pen.Width := Max(2, Side div 6);
Canvas.Brush.Style := bsClear;
{ Дуга по верхней половине окружности с центром в (12,13). }
Canvas.Arc(PX(5), PY(6), PX(19), PY(20), 0, 180 * 16);
Line(5, 13, 5, 19);
Line(19, 13, 19, 19);
Canvas.Pen.Width := 1;
Box(3, 18, 8, 23, True);
Box(16, 18, 21, 23, True);
end;
tiRipple:
begin
{ Два куска и дыра между ними; стрелка показывает, что правый
переедет влево. }
Box(1, 7, 8, 17, True);
Box(16, 7, 23, 17, True);
Tri(10, 15, 12, 4);
end;
tiLog:
begin
Box(2, 3, 22, 21, False);
Line(6, 8, 18, 8);
Line(6, 12, 18, 12);
Line(6, 16, 13, 16);
end;
end;
Canvas.Pen.Width := 1;
Canvas.Brush.Style := bsClear;
X := (ARect.Left + ARect.Right - Img.Width) div 2;
Y := (ARect.Top + ARect.Bottom - Img.Height) div 2;
Canvas.Draw(X, Y, Img);
end;
procedure TThemedButton.Paint;
@ -1286,5 +1297,6 @@ initialization
finalization
Painter.Free;
IconCache.Free;
end.

135
tools/make_icons.py Normal file
View file

@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""
Готовит модуль с данными значков из файлов в icons_online.
Зачем модулем, а не файлами рядом с программой: программа не должна
зависеть от того, лежит ли что-то в её каталоге. Значки маленькие, все
вместе занимают единицы килобайт.
Размер выбран под кнопку: у неё двадцать восемь точек стороны, значку
достаётся клетка двадцать четыре на двадцать четыре. Уменьшение делает
convert с обычным сглаживанием, пропорции при этом сохраняются — все
исходники квадратные, растягивать нечего.
Запуск: python3 tools/make_icons.py
"""
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / 'icons_online'
OUT = ROOT / 'src' / 'uIconData.pas'
# Имя в программе -> файл исходника и сторона в точках.
# Для «схлопывания» берётся вариант на шестьдесят четыре точки: чем крупнее
# исходник, тем чище получается уменьшение.
ICONS = [
('Snap', 'icons8-магнит-30.png', 24),
('Ripple', 'icons8-merge-vertical-64.png', 24),
('Log', 'icons8-property-30.png', 24),
]
# Значок самой программы: отдельно, потому что окну нужен формат ico —
# только его умеет читать TIcon в LCL.
APP_ICON = ('icons8-показ-видеокадров-30.png', 32)
def make_ico(path: Path, side: int) -> bytes:
out = subprocess.run(
['convert', str(path), '-background', 'none', '-alpha', 'on',
'-resize', '%dx%d' % (side, side), 'ico:-'],
check=True, capture_output=True)
return out.stdout
def scale(path: Path, side: int) -> bytes:
out = subprocess.run(
['convert', str(path), '-background', 'none', '-alpha', 'on',
'-resize', '%dx%d' % (side, side), 'png:-'],
check=True, capture_output=True)
return out.stdout
def pascal_bytes(data: bytes, indent: str = ' ') -> str:
parts = []
for i in range(0, len(data), 16):
chunk = ', '.join('$%02X' % b for b in data[i:i + 16])
parts.append(indent + chunk)
return ',\n'.join(parts)
def main() -> int:
if not SRC.is_dir():
print('нет каталога с исходниками:', SRC, file=sys.stderr)
return 1
blocks = []
names = []
for name, filename, side in ICONS:
path = SRC / filename
if not path.is_file():
print('нет файла:', path, file=sys.stderr)
return 1
data = scale(path, side)
names.append((name, len(data)))
blocks.append(
' { %s, %d на %d точек }\n'
' Icon%sData: array[0..%d] of Byte = (\n%s\n );'
% (filename, side, side, name, len(data) - 1, pascal_bytes(data)))
app_src, app_side = APP_ICON
app_path = SRC / app_src
if not app_path.is_file():
print('нет файла:', app_path, file=sys.stderr)
return 1
app_data = make_ico(app_path, app_side)
names.append(('App (ico)', len(app_data)))
blocks.append(
' { %s, значок окна, %d на %d точек, формат ico }\n'
' IconAppData: array[0..%d] of Byte = (\n%s\n );'
% (app_src, app_side, app_side, len(app_data) - 1,
pascal_bytes(app_data)))
text = '''{
uIconData — значки, вшитые в программу.
Собран из файлов в icons_online командой:
python3 tools/make_icons.py
Правится не он, а исходные картинки: этот файл переписывается целиком.
Значки взяты у Icons8 и требуют указания авторства — оно есть в окне
«О программе», вкладка «Библиотеки и авторство». Ссылки на каждый значок
лежат в icons_online/attribution.txt.
Картинки чёрные на прозрачном фоне. Цвет им задаётся при отрисовке:
берётся прозрачность точки, а цвет — тот, каким сейчас рисуется кнопка.
Поэтому один и тот же значок годится и для тёмной кнопки, и для
оранжевой.
}
unit uIconData;
{$mode objfpc}{$H+}
interface
const
%s
implementation
end.
''' % ('\n\n'.join(blocks))
OUT.write_text(text, encoding='utf-8')
print('записан', OUT)
for name, size in names:
print(' %-8s %d байт' % (name, size))
return 0
if __name__ == '__main__':
sys.exit(main())