Курсор воспроизведения по щелчку вставал ровно туда, куда попала мышь. В стык двух кусков так не попасть: на общем плане одна точка экрана — это секунды материала, а ставят курсор чаще всего именно на стык. Теперь он притягивается к границам клипов на любой дорожке и к началу монтажа. Работает и при щелчке по клипу, и по линейке, и по пустому месту, и при протяжке. Shift меняет решение на противоположное — как у прилипания клипов. Переключатель свой, со значком клея, рядом с магнитом: клипы липнут при перетаскивании, курсор — при щелчке, это разные привычки, и одно бывает нужно без другого. Состояние переживает перезапуск. Расчёт открыт наружу и проверяется числами: семь проверок в lfmcheck, от «стык рядом — притянуло» до «выключено — не притягивает».
235 lines
10 KiB
ObjectPascal
235 lines
10 KiB
ObjectPascal
{
|
||
uAboutForm — окно «О программе».
|
||
|
||
Две вкладки. «Ресурсы» — на чём программа держится и чьи значки
|
||
использует: бесплатная лицензия Icons8 требует ссылку на каждый значок.
|
||
«Возможности» — что программа умеет.
|
||
|
||
Вкладки свои, а не TPageControl: системные под Windows остались бы
|
||
светлой полосой поверх тёмного окна.
|
||
|
||
Текст показывает uArticleView: он сам переносит строки по ширине окна и
|
||
делает ссылки нажимаемыми.
|
||
}
|
||
unit uAboutForm;
|
||
|
||
{$mode objfpc}{$H+}
|
||
|
||
interface
|
||
|
||
uses
|
||
Classes, SysUtils, Controls, Forms, Graphics, StdCtrls, Dialogs,
|
||
uTheme, uArticleView;
|
||
|
||
procedure ShowAboutForm;
|
||
|
||
implementation
|
||
|
||
type
|
||
{ Обработчики живут в объекте: событиям нужны методы. }
|
||
TAboutDialog = class(TComponent)
|
||
public
|
||
Form: TForm;
|
||
BtnResources: TThemedButton;
|
||
BtnFeatures: TThemedButton;
|
||
ViewResources: TArticleView;
|
||
ViewFeatures: TArticleView;
|
||
procedure ShowResources(Sender: TObject);
|
||
procedure ShowFeatures(Sender: TObject);
|
||
procedure Select(AResources: Boolean);
|
||
end;
|
||
|
||
procedure TAboutDialog.Select(AResources: Boolean);
|
||
begin
|
||
BtnResources.Down := AResources;
|
||
BtnFeatures.Down := not AResources;
|
||
ViewResources.Visible := AResources;
|
||
ViewFeatures.Visible := not AResources;
|
||
end;
|
||
|
||
procedure TAboutDialog.ShowResources(Sender: TObject);
|
||
begin
|
||
Select(True);
|
||
end;
|
||
|
||
procedure TAboutDialog.ShowFeatures(Sender: TObject);
|
||
begin
|
||
Select(False);
|
||
end;
|
||
|
||
{ Списки под Windows и под Linux разные: там сборки скачивает сама
|
||
программа, здесь их ставит пакетный менеджер. }
|
||
function ResourcesText: string;
|
||
begin
|
||
Result :=
|
||
'# nlve' + LineEnding +
|
||
'Монтаж без перекодирования: куски вырезаются и склеиваются ' +
|
||
'копированием потока.' + LineEnding +
|
||
'Автор: 4aiman' + LineEnding +
|
||
LineEnding +
|
||
'# Значки' + LineEnding +
|
||
'Icons8. Нужна ссылка на каждый значок.' + LineEnding +
|
||
'Прилипание — Magnet' + LineEnding +
|
||
' https://icons8.com/icon/79942/magnet' + LineEnding +
|
||
'Схлопывание — Merge Vertical' + LineEnding +
|
||
' https://icons8.com/icon/2EQegp8tbR6Q/merge-vertical' + LineEnding +
|
||
'Журнал — Property' + LineEnding +
|
||
' https://icons8.com/icon/79872/property' + LineEnding +
|
||
'Прилипание курсора — Клей' + LineEnding +
|
||
' https://icons8.com/icon/111296/glue' + LineEnding +
|
||
'Значок программы — Showing Video Frames' + LineEnding +
|
||
' https://icons8.com/icon/69093/showing-video-frames' + LineEnding +
|
||
'Сам Icons8' + LineEnding +
|
||
' https://icons8.com' + LineEnding +
|
||
LineEnding +
|
||
'# Видео и звук' + LineEnding +
|
||
{$IFDEF WINDOWS}
|
||
'ffmpeg — нарезка, склейка, перекодирование. Сборка BtbN, GPL.' +
|
||
LineEnding +
|
||
' https://github.com/BtbN/FFmpeg-Builds' + LineEnding +
|
||
' https://ffmpeg.org' + LineEnding +
|
||
'libmpv — предпросмотр. Сборка shinchiro, GPL.' + LineEnding +
|
||
' https://github.com/shinchiro/mpv-winbuild-cmake' + LineEnding +
|
||
' https://mpv.io' + LineEnding +
|
||
'7-Zip — распаковка сборок libmpv. LGPL-2.1.' + LineEnding +
|
||
' https://www.7-zip.org' + LineEnding +
|
||
{$ELSE}
|
||
'ffmpeg — нарезка, склейка, перекодирование. GPL-2 и новее.' +
|
||
LineEnding +
|
||
' sudo apt install ffmpeg' + LineEnding +
|
||
' https://ffmpeg.org' + LineEnding +
|
||
'libmpv — предпросмотр. GPL-3 и новее.' + LineEnding +
|
||
' sudo apt install mpv' + LineEnding +
|
||
' https://mpv.io' + LineEnding +
|
||
{$ENDIF}
|
||
LineEnding +
|
||
'# Инструменты' + LineEnding +
|
||
'Free Pascal' + LineEnding +
|
||
' https://www.freepascal.org' + LineEnding +
|
||
'Lazarus LCL' + LineEnding +
|
||
' https://www.lazarus-ide.org' + LineEnding +
|
||
LineEnding +
|
||
'ffmpeg и libmpv в состав программы не входят: ffmpeg запускается ' +
|
||
'отдельной программой, libmpv ставится отдельно.';
|
||
end;
|
||
|
||
function FeaturesText: string;
|
||
begin
|
||
Result :=
|
||
'# Монтаж' + LineEnding +
|
||
'Дорожки для картинки и для звука.' + LineEnding +
|
||
'При перекрытии картинку даёт верхняя дорожка, звук всех дорожек ' +
|
||
'смешивается.' + LineEnding +
|
||
'Перенос, растягивание за края, рез по курсору (S), удаление.' +
|
||
LineEnding +
|
||
'Прилипание к соседям и к курсору. Shift отменяет его.' + LineEnding +
|
||
'Связки: видео и его звук ходят вместе. Ctrl+G связывает и ' +
|
||
'развязывает.' + LineEnding +
|
||
'Схлопывание дыр: Ctrl+Shift+R или само при перетаскивании.' +
|
||
LineEnding +
|
||
'Отмена и повтор: Ctrl+Z, Ctrl+Y.' + LineEnding +
|
||
LineEnding +
|
||
'# На дорожках' + LineEnding +
|
||
'Форма звука. Shift со стрелками усиливает её на тихих записях.' +
|
||
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 +
|
||
'Индекс опорных кадров читается из заголовка: 20 мс вместо 12 секунд.' +
|
||
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 := 420;
|
||
Dlg.Form.Constraints.MinHeight := 300;
|
||
|
||
Dlg.BtnResources := TThemedButton.Create(Dlg.Form);
|
||
Dlg.BtnResources.Parent := Dlg.Form;
|
||
Dlg.BtnResources.SetBounds(12, 12, 140, 28);
|
||
Dlg.BtnResources.Caption := 'Ресурсы';
|
||
Dlg.BtnResources.TabStop := False;
|
||
Dlg.BtnResources.OnClick := @Dlg.ShowResources;
|
||
|
||
Dlg.BtnFeatures := TThemedButton.Create(Dlg.Form);
|
||
Dlg.BtnFeatures.Parent := Dlg.Form;
|
||
Dlg.BtnFeatures.SetBounds(158, 12, 140, 28);
|
||
Dlg.BtnFeatures.Caption := 'Возможности';
|
||
Dlg.BtnFeatures.TabStop := False;
|
||
Dlg.BtnFeatures.OnClick := @Dlg.ShowFeatures;
|
||
|
||
Dlg.ViewResources := TArticleView.Create(Dlg.Form);
|
||
Dlg.ViewResources.Parent := Dlg.Form;
|
||
Dlg.ViewResources.SetBounds(12, 48, 696, 460);
|
||
Dlg.ViewResources.Anchors := [akTop, akLeft, akRight, akBottom];
|
||
|
||
Dlg.ViewFeatures := TArticleView.Create(Dlg.Form);
|
||
Dlg.ViewFeatures.Parent := Dlg.Form;
|
||
Dlg.ViewFeatures.SetBounds(12, 48, 696, 460);
|
||
Dlg.ViewFeatures.Anchors := [akTop, akLeft, akRight, akBottom];
|
||
|
||
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.ViewResources.SetText(ResourcesText);
|
||
Dlg.ViewFeatures.SetText(FeaturesText);
|
||
Dlg.Select(True);
|
||
|
||
Dlg.Form.ShowModal;
|
||
finally
|
||
Dlg.Form.Free;
|
||
Dlg.Free;
|
||
end;
|
||
end;
|
||
|
||
end.
|