nlve/src/uAboutForm.pas
4aiman 58d7748d51 Значки Icons8, окно «О программе» с указанием авторства
Значки заменены на присланные. Они вшиты в программу модулем uIconData,
который собирается из icons_online командой tools/make_icons.py — правятся
исходные картинки, а не он. Размер выбран под кнопку: двадцать четыре
точки в клетке двадцать восемь, уменьшение делает convert, пропорции
сохранены — все исходники квадратные.

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

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

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

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

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

Свои рисованные значки из icons/ убраны: они больше не используются, и
держать рядом два набора значило бы путать.
2026-08-22 10:54:30 +03:00

347 lines
18 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
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.