Значки заменены на присланные. Они вшиты в программу модулем uIconData, который собирается из icons_online командой tools/make_icons.py — правятся исходные картинки, а не он. Размер выбран под кнопку: двадцать четыре точки в клетке двадцать восемь, уменьшение делает convert, пропорции сохранены — все исходники квадратные. Цвет значку задаётся при отрисовке: берётся прозрачность точки, а цвет — тот, каким сейчас рисуется кнопка. Поэтому один значок годится и для тёмной кнопки, и для оранжевой, и для недоступной. Перекрашенные запоминаются: цветов ровно три. Значок программы стал значком окна. Значок самого файла под Windows задаётся не отсюда, а ресурсом при сборке — это осталось как было. Окно «О программе» (Справка → О программе). Две вкладки: «Библиотеки и авторство» и «Что умеет». Первая обязательна — бесплатная лицензия Icons8 требует ссылку на каждый значок; заодно там перечислено всё, на чём программа держится, и под какой лицензией. Списки разные по системам: под Linux ffmpeg и libmpv ставит пакетный менеджер, под Windows их скачивает сама программа, и это разные сборки. Вкладки свои, а не TPageControl: системные вкладки под Windows остались бы светлой полосой поверх тёмного окна. Текст показывается нашим журналом — его можно выделить и скопировать, так что ссылку из списка получится взять в браузер. Перенос по словам свой: ссылки не рвутся, отступ списка сохраняется на продолжениях. Свои рисованные значки из icons/ убраны: они больше не используются, и держать рядом два набора значило бы путать.
347 lines
18 KiB
ObjectPascal
347 lines
18 KiB
ObjectPascal
{
|
||
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.
|