Выделение по времени, рез по краям промежутка, курсор идёт за резом
Режимов выделения стало два, переключаются клавишей D и кнопкой внизу, режим показан её значком. Рамкой по клипам — как было. По времени — протяжка отмечает промежуток по всем дорожкам сразу, он остаётся виден пунктиром, и S режет все дорожки по обоим его краям. Выделенной после этого остаётся ровно середина: следующим действием её обычно удаляют, а выделение на старых клипах убрало бы не то. Курсор теперь идёт за резом: после S встаёт на место реза (в режиме по времени — на начало промежутка), после удаления куска — на место стыка, после схлопывания дыры — туда же. Раньше в это место приходилось попадать мышью заново. Рез кладётся на кадровую сетку: между кадрами времени нет, и рез на доле кадра оставлял бы в монтаже время, которого не бывает. Выделение рамкой требует настоящего перекрытия, а не касания краями: сосед, кончающийся ровно на краю рамки, попадал в выделение из-за разницы в последнем знаке дробей. Кнопка журнала и строка состояния сдвинуты вправо: переключатель выделения встал в один ряд с прилипанием и схлопыванием. Значки — icons8, набор ios-glyphs, авторство дописано в attribution.txt. Набора «ios26» на их раздаче нет (404), взят ближайший по виду к нынешним. Проверки: рез по курсору и по краям промежутка, кадровая сетка, выделение середины, курсор после удаления и после схлопывания.
This commit is contained in:
parent
37787ce96c
commit
6617e29514
11 changed files with 538 additions and 31 deletions
27
README.md
27
README.md
|
|
@ -81,6 +81,7 @@ xdg-mime query default application/x-nlve-project
|
||||||
| Удалить выделенное | Delete |
|
| Удалить выделенное | Delete |
|
||||||
| Выделить всё | Ctrl+A |
|
| Выделить всё | Ctrl+A |
|
||||||
| Выделить областью | протяжка по пустому месту дорожек |
|
| Выделить областью | протяжка по пустому месту дорожек |
|
||||||
|
| Режим выделения: рамкой или по времени | D |
|
||||||
| Схлопнуть дыру под курсором | Ctrl+Shift+R |
|
| Схлопнуть дыру под курсором | Ctrl+Shift+R |
|
||||||
| Отменить, повторить | Ctrl+Z, Ctrl+Y |
|
| Отменить, повторить | Ctrl+Z, Ctrl+Y |
|
||||||
| Масштаб таймлайна | Ctrl + колесо мыши |
|
| Масштаб таймлайна | Ctrl + колесо мыши |
|
||||||
|
|
@ -272,6 +273,32 @@ Ctrl+Shift+R, который схлопывает дыру под курсоро
|
||||||
удаляет: она лежит, пока проект не сохранят заново. После сохранения
|
удаляет: она лежит, пока проект не сохранят заново. После сохранения
|
||||||
копия не нужна и убирается.
|
копия не нужна и убирается.
|
||||||
|
|
||||||
|
## Два режима выделения
|
||||||
|
|
||||||
|
Кнопка внизу (справа от схлопывания) и клавиша `D`. Режим показан
|
||||||
|
значком: рамка — выделение клипов, промежуток со стрелкой — выделение по
|
||||||
|
времени.
|
||||||
|
|
||||||
|
**Рамкой по клипам.** Протяжка по пустому месту дорожек выделяет всё, что
|
||||||
|
рамка задела. Обычное выделение: выделил, нажал Delete.
|
||||||
|
|
||||||
|
**По времени.** Протяжка отмечает промежуток от и до — по всем дорожкам
|
||||||
|
сразу, сколько бы их ни было. Промежуток виден пунктиром и после того,
|
||||||
|
как кнопку отпустили: по нему режет `S`.
|
||||||
|
|
||||||
|
Зачем второй режим: вырезать из фильма кусок целиком. Отметил промежуток,
|
||||||
|
нажал `S` — все дорожки разрезаны в двух местах, по краям промежутка, и
|
||||||
|
выделенной осталась ровно середина. Дальше Delete, и при включённом
|
||||||
|
автосхлопывании дыра закрывается сама.
|
||||||
|
|
||||||
|
Края промежутка кладутся на кадровую сетку: между кадрами времени нет.
|
||||||
|
|
||||||
|
**Курсор идёт за резом.** После `S` он встаёт ровно на место реза (в
|
||||||
|
режиме по времени — на начало промежутка), после удаления куска и
|
||||||
|
схлопывания — на место стыка. Дальше с этим местом и работают: следующий
|
||||||
|
рез, схлопывание дыры, просмотр перехода. Раньше курсор оставался там, где
|
||||||
|
был, и в место реза приходилось попадать мышью заново.
|
||||||
|
|
||||||
## Починка проекта
|
## Починка проекта
|
||||||
|
|
||||||
Меню «Проект» → **Проверить и починить проект...**. Показывает, что нашла,
|
Меню «Проект» → **Проверить и починить проект...**. Показывает, что нашла,
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,6 @@ Log panel: <a target="_blank" href="https://icons8.com/icon/79872/property">Prop
|
||||||
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>
|
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>
|
Magnet: <a target="_blank" href="https://icons8.com/icon/79942/magnet">Magnet</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
|
||||||
Playhead snapping: <a target="_blank" href="https://icons8.com/icon/111296/glue">Клей</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
|
Playhead snapping: <a target="_blank" href="https://icons8.com/icon/111296/glue">Клей</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>
|
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>
|
||||||
|
Selection mode, clips: <a target="_blank" href="https://icons8.com/icon/set/select-all/ios-glyphs">Select All</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
|
||||||
|
Selection mode, time range: <a target="_blank" href="https://icons8.com/icon/set/width/ios-glyphs">Width</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
|
||||||
|
|
|
||||||
BIN
icons_online/icons8-select-all-90.png
Normal file
BIN
icons_online/icons8-select-all-90.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 476 B |
BIN
icons_online/icons8-width-90.png
Normal file
BIN
icons_online/icons8-width-90.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 656 B |
|
|
@ -32,8 +32,8 @@ const
|
||||||
$60, $00, $00, $3A, $98, $00, $00, $17, $70, $9C, $BA, $51, $3C, $00, $00, $00,
|
$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,
|
$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,
|
$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, $1F, $07, $2E, $1B, $C7,
|
$00, $00, $00, $07, $74, $49, $4D, $45, $07, $EA, $09, $1D, $14, $21, $19, $27,
|
||||||
$2C, $48, $09, $00, $00, $01, $3E, $49, $44, $41, $54, $38, $CB, $BD, $92, $31,
|
$B3, $C9, $F8, $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,
|
$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,
|
$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,
|
$F8, $39, $EC, $62, $06, $17, $5D, $EA, $22, $D2, $88, $93, $E0, $22, $68, $A1,
|
||||||
|
|
@ -66,8 +66,8 @@ const
|
||||||
$60, $00, $00, $3A, $98, $00, $00, $17, $70, $9C, $BA, $51, $3C, $00, $00, $00,
|
$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,
|
$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,
|
$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, $1F, $07, $2E, $1B, $C7,
|
$00, $00, $00, $07, $74, $49, $4D, $45, $07, $EA, $09, $1D, $14, $21, $19, $27,
|
||||||
$2C, $48, $09, $00, $00, $01, $2F, $49, $44, $41, $54, $38, $CB, $CD, $D2, $3D,
|
$B3, $C9, $F8, $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,
|
$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,
|
$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,
|
$31, $98, $18, $99, $25, $44, $F8, $02, $26, $B1, $30, $30, $78, $D9, $90, $18,
|
||||||
|
|
@ -99,8 +99,8 @@ const
|
||||||
$60, $00, $00, $3A, $98, $00, $00, $17, $70, $9C, $BA, $51, $3C, $00, $00, $00,
|
$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,
|
$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,
|
$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, $1F, $07, $2E, $1B, $C7,
|
$00, $00, $00, $07, $74, $49, $4D, $45, $07, $EA, $09, $1D, $14, $21, $19, $27,
|
||||||
$2C, $48, $09, $00, $00, $01, $0F, $49, $44, $41, $54, $38, $CB, $B5, $D3, $BF,
|
$B3, $C9, $F8, $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,
|
$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,
|
$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,
|
$2B, $30, $18, $24, $4C, $0D, $8B, $41, $13, $46, $62, $90, $18, $A4, $4B, $55,
|
||||||
|
|
@ -130,8 +130,8 @@ const
|
||||||
$60, $00, $00, $3A, $98, $00, $00, $17, $70, $9C, $BA, $51, $3C, $00, $00, $00,
|
$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,
|
$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,
|
$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, $1F, $07, $2E, $1B, $C7,
|
$00, $00, $00, $07, $74, $49, $4D, $45, $07, $EA, $09, $1D, $14, $21, $19, $27,
|
||||||
$2C, $48, $09, $00, $00, $01, $67, $49, $44, $41, $54, $38, $CB, $B5, $D3, $3D,
|
$B3, $C9, $F8, $00, $00, $01, $67, $49, $44, $41, $54, $38, $CB, $B5, $D3, $3D,
|
||||||
$4F, $93, $61, $14, $C6, $F1, $5F, $AB, $80, $9D, $A8, $A1, $71, $95, $08, $14,
|
$4F, $93, $61, $14, $C6, $F1, $5F, $AB, $80, $9D, $A8, $A1, $71, $95, $08, $14,
|
||||||
$3F, $80, $71, $D2, $8D, $C5, $D8, $A4, $A6, $61, $31, $74, $32, $31, $1A, $99,
|
$3F, $80, $71, $D2, $8D, $C5, $D8, $A4, $A6, $61, $31, $74, $32, $31, $1A, $99,
|
||||||
$89, $B3, $8B, $71, $B0, $7C, $0F, $37, $42, $4B, $07, $07, $D0, $98, $30, $E0,
|
$89, $B3, $8B, $71, $B0, $7C, $0F, $37, $42, $4B, $07, $07, $D0, $98, $30, $E0,
|
||||||
|
|
@ -167,8 +167,8 @@ const
|
||||||
$60, $00, $00, $3A, $98, $00, $00, $17, $70, $9C, $BA, $51, $3C, $00, $00, $00,
|
$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,
|
$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,
|
$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, $1F, $07, $2E, $1B, $C7,
|
$00, $00, $00, $07, $74, $49, $4D, $45, $07, $EA, $09, $1D, $14, $21, $19, $27,
|
||||||
$2C, $48, $09, $00, $00, $02, $71, $49, $44, $41, $54, $38, $CB, $95, $D4, $41,
|
$B3, $C9, $F8, $00, $00, $02, $71, $49, $44, $41, $54, $38, $CB, $95, $D4, $41,
|
||||||
$68, $DB, $65, $18, $06, $F0, $5F, $E2, $DA, $2A, $2E, $59, $B2, $65, $29, $4B,
|
$68, $DB, $65, $18, $06, $F0, $5F, $E2, $DA, $2A, $2E, $59, $B2, $65, $29, $4B,
|
||||||
$B4, $A7, $C9, $B0, $DB, $A0, $8C, $49, $63, $55, $50, $0F, $56, $31, $ED, $68,
|
$B4, $A7, $C9, $B0, $DB, $A0, $8C, $49, $63, $55, $50, $0F, $56, $31, $ED, $68,
|
||||||
$BA, $CB, $70, $3B, $C8, $F0, $24, $08, $0A, $1E, $44, $99, $4E, $AA, $B2, $5D,
|
$BA, $CB, $70, $3B, $C8, $F0, $24, $08, $0A, $1E, $44, $99, $4E, $AA, $B2, $5D,
|
||||||
|
|
@ -211,6 +211,76 @@ const
|
||||||
$00, $00, $00, $00, $49, $45, $4E, $44, $AE, $42, $60, $82
|
$00, $00, $00, $00, $49, $45, $4E, $44, $AE, $42, $60, $82
|
||||||
);
|
);
|
||||||
|
|
||||||
|
{ icons8-select-all-90.png, 24 на 24 точек }
|
||||||
|
IconSelectClipsData: array[0..509] 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, $09, $1D, $14, $21, $19, $27,
|
||||||
|
$B3, $C9, $F8, $00, $00, $01, $63, $49, $44, $41, $54, $38, $CB, $B5, $D3, $BD,
|
||||||
|
$6E, $13, $41, $14, $05, $E0, $6F, $76, $13, $7B, $17, $21, $A4, $98, $48, $34,
|
||||||
|
$3C, $00, $05, $0A, $52, $78, $05, $AA, $50, $24, $8F, $C1, $1B, $E4, $1D, $D2,
|
||||||
|
$51, $41, $49, $43, $EB, $CE, $A9, $90, $A5, $28, $D6, $26, $15, $55, $8A, $3C,
|
||||||
|
$00, $42, $32, $08, $2A, $62, $25, $CE, $7A, $87, $C2, $E3, $3F, $61, $0A, $17,
|
||||||
|
$39, $53, $CC, $E8, $9E, $39, $33, $F7, $DC, $99, $CB, $43, $23, $A4, $79, $CB,
|
||||||
|
$36, $EE, $4D, $B4, $05, $DC, $A1, $8D, $E8, $4E, $6E, $1B, $63, $93, $85, $2C,
|
||||||
|
$77, $EC, $5C, $E5, $C8, $AE, $AE, $4A, $DF, $9E, $3D, $7D, $95, $AE, $5D, $47,
|
||||||
|
$2A, $E7, $8E, $E5, $90, $81, $96, $03, $FB, $32, $71, $6D, $16, $51, $66, $DF,
|
||||||
|
$81, $D6, $22, $54, $1A, $B8, $B0, $23, $17, $14, $4A, $A5, $4C, $A6, $54, $2A,
|
||||||
|
$04, $B9, $1D, $17, $06, $CA, $69, $32, $53, $FC, $34, $70, $65, $82, $5A, $AD,
|
||||||
|
$16, $C5, $B4, $22, $AA, $FD, $50, $B9, $D6, $CC, $4C, $07, $4F, $F1, $4B, $14,
|
||||||
|
$74, $3C, $9E, $A7, $16, $FC, $F1, $5B, $5C, $E2, $13, $0A, $5D, $5D, $05, $3A,
|
||||||
|
$4E, $0D, $7D, $4F, $63, $E8, $54, $67, $85, $B7, $95, $4E, $7A, $96, $4A, $7C,
|
||||||
|
$EB, $8B, $6F, $9A, $14, $0D, $AE, $DD, $AE, $F0, $49, $30, $AD, $3B, $8C, $BC,
|
||||||
|
$9F, $C7, $EE, $93, $70, $99, $4F, $1E, $32, $2F, $71, $A5, $F1, $C4, $89, $57,
|
||||||
|
$1A, $44, $1F, $7C, $4E, $BB, $16, $7C, $7A, $87, $8D, $51, $EA, $EB, $4F, $EB,
|
||||||
|
$2C, $D7, $4E, $23, $5B, $C7, $CF, $F2, $6D, $A7, $F9, $91, $77, $5E, $AC, $98,
|
||||||
|
$FE, $68, $B4, $C4, $27, $41, $34, $44, $44, $E1, $8D, $D7, $F3, $6F, $96, $7B,
|
||||||
|
$EE, $93, $D1, $12, $BF, $F9, $C3, $85, $74, $CF, $5B, $F4, $D4, $FF, $F1, $F8,
|
||||||
|
$0F, $BF, $C1, $E7, $9B, $09, $CE, $DC, $B8, $74, $B8, $B6, $1F, $0E, $5D, $BA,
|
||||||
|
$71, $B6, $5C, $A5, $B1, $9E, $4C, $3E, $EF, $BF, $55, $04, $8D, $AF, $7A, $C6,
|
||||||
|
$0B, $D3, $E4, $5A, $36, $68, $D1, $87, $C4, $5F, $C8, $81, $83, $A2, $85, $86,
|
||||||
|
$DA, $DD, $00, $00, $00, $00, $49, $45, $4E, $44, $AE, $42, $60, $82
|
||||||
|
);
|
||||||
|
|
||||||
|
{ icons8-width-90.png, 24 на 24 точек }
|
||||||
|
IconSelectRangeData: array[0..473] 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, $09, $1D, $14, $21, $19, $27,
|
||||||
|
$B3, $C9, $F8, $00, $00, $01, $3F, $49, $44, $41, $54, $38, $CB, $CD, $D2, $BF,
|
||||||
|
$4A, $1C, $51, $18, $05, $F0, $DF, $8C, $3B, $8E, $53, $64, $70, $94, $2D, $5D,
|
||||||
|
$B0, $90, $B0, $C5, $DA, $A6, $14, $C1, $28, $E8, $1B, $F8, $0C, $61, $FB, $7D,
|
||||||
|
$02, $5F, $40, $D8, $07, $91, $6D, $92, $52, $2D, $6C, $BD, $45, $08, $29, $84,
|
||||||
|
$ED, $C4, $C2, $90, $26, $8C, $6E, $66, $53, $B8, $7F, $26, $82, $85, $0A, $E2,
|
||||||
|
$B9, $C5, $2D, $CE, $3D, $DF, $F7, $9D, $F3, $5D, $DE, $1D, $16, $26, $77, $2E,
|
||||||
|
$55, $CA, $7C, $50, $4A, $14, $4A, $91, $15, $23, $63, $85, $C8, $68, $C2, $CF,
|
||||||
|
$D0, $D0, $D7, $17, $EB, $3A, $91, $DB, $73, $66, $C3, $86, $33, $7B, $72, $27,
|
||||||
|
$BA, $62, $7D, $7D, $8D, $87, $A7, $90, $D8, $44, $22, $A0, $34, $34, $F0, $0B,
|
||||||
|
$03, $43, $A5, $6F, $C2, $8C, $1F, $4D, $3B, $64, $CE, $9D, $CB, $9E, $1C, $BC,
|
||||||
|
$C6, $C7, $CF, $35, $FD, $2A, $C1, $58, $C7, $BE, $C5, $FF, $F8, $45, $FB, $3A,
|
||||||
|
$C6, $F5, $7C, $A6, $A8, $7C, $72, $AC, $12, $DC, $88, $66, $25, $9A, $8E, $C4,
|
||||||
|
$BE, $A8, $A6, $A5, $A3, $89, $A9, $AF, $D6, $FD, $D6, $76, $EB, $87, $BF, $35,
|
||||||
|
$C1, $82, $8F, $0A, $DF, $E5, $AE, $EC, $FA, $F3, $02, $0F, $F3, $D8, $4E, $6D,
|
||||||
|
$09, $2E, $AD, $59, $92, $4D, $CE, $92, $35, $97, $82, $2D, $A7, $D3, $58, $E7,
|
||||||
|
$1E, $62, $17, $0E, $B5, $5C, $BB, $AB, $95, $BA, $D6, $33, $F4, $73, $3E, $49,
|
||||||
|
$A3, $46, $46, $82, $F0, $A8, $F7, $9D, $01, $F5, $95, $BE, $ED, $E2, $2A, $DB,
|
||||||
|
$BA, $52, $6D, $3D, $4D, $4D, $3D, $6D, $A9, $AE, $6D, $D5, $63, $C1, $48, $10,
|
||||||
|
$DC, $EB, $D8, $91, $6A, $39, $B0, $6C, $D9, $81, $96, $D4, $67, $1D, $F7, $82,
|
||||||
|
$30, $FF, $AB, $90, $CB, $91, $29, $44, $12, $AB, $62, $B1, $55, $89, $48, $21,
|
||||||
|
$9B, $F1, $EF, $13, $FF, $00, $8F, $00, $4E, $D0, $9E, $28, $A1, $C0, $00, $00,
|
||||||
|
$00, $00, $49, $45, $4E, $44, $AE, $42, $60, $82
|
||||||
|
);
|
||||||
|
|
||||||
{ icons8-показ-видеокадров-30.png, значок окна, 32 на 32 точек, формат ico }
|
{ icons8-показ-видеокадров-30.png, значок окна, 32 на 32 точек, формат ico }
|
||||||
IconAppData: array[0..4285] of Byte = (
|
IconAppData: array[0..4285] of Byte = (
|
||||||
$00, $00, $01, $00, $01, $00, $20, $20, $00, $00, $01, $00, $20, $00, $A8, $10,
|
$00, $00, $01, $00, $01, $00, $20, $20, $00, $00, $01, $00, $20, $00, $A8, $10,
|
||||||
|
|
|
||||||
|
|
@ -97,11 +97,21 @@ object MainForm: TMainForm
|
||||||
OnClick = btnRippleMenuClick
|
OnClick = btnRippleMenuClick
|
||||||
TabStop = False
|
TabStop = False
|
||||||
end
|
end
|
||||||
|
object btnSelectMode: TThemedButton
|
||||||
|
Left = 448
|
||||||
|
Height = 28
|
||||||
|
Top = 6
|
||||||
|
Width = 28
|
||||||
|
Icon = tiSelectClips
|
||||||
|
OnClick = btnSelectModeClick
|
||||||
|
ShowHint = True
|
||||||
|
TabStop = False
|
||||||
|
end
|
||||||
object lblStatus: TLabel
|
object lblStatus: TLabel
|
||||||
Left = 488
|
Left = 518
|
||||||
Height = 20
|
Height = 20
|
||||||
Top = 10
|
Top = 10
|
||||||
Width = 780
|
Width = 750
|
||||||
Anchors = [akTop, akLeft, akRight]
|
Anchors = [akTop, akLeft, akRight]
|
||||||
AutoSize = False
|
AutoSize = False
|
||||||
Caption = 'Готов.'
|
Caption = 'Готов.'
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ type
|
||||||
btnCenter: TThemedButton;
|
btnCenter: TThemedButton;
|
||||||
btnRipple: TThemedButton;
|
btnRipple: TThemedButton;
|
||||||
btnRippleMenu: TThemedButton;
|
btnRippleMenu: TThemedButton;
|
||||||
|
btnSelectMode: TThemedButton;
|
||||||
lblPoolInfo: TLabel;
|
lblPoolInfo: TLabel;
|
||||||
lblStatus: TLabel;
|
lblStatus: TLabel;
|
||||||
lblTime: TLabel;
|
lblTime: TLabel;
|
||||||
|
|
@ -72,6 +73,7 @@ type
|
||||||
procedure btnCenterClick(Sender: TObject);
|
procedure btnCenterClick(Sender: TObject);
|
||||||
procedure btnRippleClick(Sender: TObject);
|
procedure btnRippleClick(Sender: TObject);
|
||||||
procedure btnRippleMenuClick(Sender: TObject);
|
procedure btnRippleMenuClick(Sender: TObject);
|
||||||
|
procedure btnSelectModeClick(Sender: TObject);
|
||||||
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
|
||||||
procedure FormCreate(Sender: TObject);
|
procedure FormCreate(Sender: TObject);
|
||||||
procedure FormDestroy(Sender: TObject);
|
procedure FormDestroy(Sender: TObject);
|
||||||
|
|
@ -156,6 +158,7 @@ type
|
||||||
{ Пункт «Ключ и прозрачность в предпросмотре»: галку в нём приходится
|
{ Пункт «Ключ и прозрачность в предпросмотре»: галку в нём приходится
|
||||||
держать за руку — она не следует за настройкой сама. }
|
держать за руку — она не следует за настройкой сама. }
|
||||||
FKeyPreviewItem: TMenuItem;
|
FKeyPreviewItem: TMenuItem;
|
||||||
|
FSelectModeItem: TMenuItem;
|
||||||
{ Показанная доля выполнения. Отдельно от настоящей, потому что растёт
|
{ Показанная доля выполнения. Отдельно от настоящей, потому что растёт
|
||||||
она по своим правилам — см. AdvanceJobBar. }
|
она по своим правилам — см. AdvanceJobBar. }
|
||||||
FJobShown: Double;
|
FJobShown: Double;
|
||||||
|
|
@ -240,6 +243,8 @@ type
|
||||||
procedure TrackMenuDown(Sender: TObject);
|
procedure TrackMenuDown(Sender: TObject);
|
||||||
procedure UpdateSnapButton;
|
procedure UpdateSnapButton;
|
||||||
procedure UpdateGlueButton;
|
procedure UpdateGlueButton;
|
||||||
|
procedure UpdateSelectModeButton;
|
||||||
|
procedure TimelineSelectModeChange(Sender: TObject);
|
||||||
procedure UpdateCenterButton;
|
procedure UpdateCenterButton;
|
||||||
procedure ToggleCenterPlayhead;
|
procedure ToggleCenterPlayhead;
|
||||||
procedure RippleModeChosen(Sender: TObject);
|
procedure RippleModeChosen(Sender: TObject);
|
||||||
|
|
@ -372,6 +377,7 @@ type
|
||||||
{ Что и откуда собирать: с учётом отмеченного участка. }
|
{ Что и откуда собирать: с учётом отмеченного участка. }
|
||||||
procedure DescribeRegion(out AFrom, ATo: Double);
|
procedure DescribeRegion(out AFrom, ATo: Double);
|
||||||
procedure MenuSelectAll(Sender: TObject);
|
procedure MenuSelectAll(Sender: TObject);
|
||||||
|
procedure MenuSelectMode(Sender: TObject);
|
||||||
procedure MenuRipple(Sender: TObject);
|
procedure MenuRipple(Sender: TObject);
|
||||||
procedure MenuToggleRipple(Sender: TObject);
|
procedure MenuToggleRipple(Sender: TObject);
|
||||||
procedure MenuGoStart(Sender: TObject);
|
procedure MenuGoStart(Sender: TObject);
|
||||||
|
|
@ -525,6 +531,7 @@ begin
|
||||||
UpdateRippleButton;
|
UpdateRippleButton;
|
||||||
UpdateSnapButton;
|
UpdateSnapButton;
|
||||||
UpdateGlueButton;
|
UpdateGlueButton;
|
||||||
|
UpdateSelectModeButton;
|
||||||
UpdateCenterButton;
|
UpdateCenterButton;
|
||||||
UpdateCaption;
|
UpdateCaption;
|
||||||
SetStatus('Готов. Добавьте файлы в проект.');
|
SetStatus('Готов. Добавьте файлы в проект.');
|
||||||
|
|
@ -662,6 +669,7 @@ begin
|
||||||
FTimeline.OnTrackMenu := @TimelineTrackMenu;
|
FTimeline.OnTrackMenu := @TimelineTrackMenu;
|
||||||
FTimeline.OnTrackRename := @TimelineTrackRename;
|
FTimeline.OnTrackRename := @TimelineTrackRename;
|
||||||
FTimeline.OnRegionChange := @TimelineRegionChange;
|
FTimeline.OnRegionChange := @TimelineRegionChange;
|
||||||
|
FTimeline.OnSelectionModeChange := @TimelineSelectModeChange;
|
||||||
FTimeline.OnActiveTrackChange := @TimelineActiveTrackChange;
|
FTimeline.OnActiveTrackChange := @TimelineActiveTrackChange;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -768,6 +776,10 @@ begin
|
||||||
AddItem(MEdit, 'Разрезать по курсору', @MenuSplit, ShortCut(VK_S, []));
|
AddItem(MEdit, 'Разрезать по курсору', @MenuSplit, ShortCut(VK_S, []));
|
||||||
AddItem(MEdit, 'Удалить выделенное', @MenuDelete, ShortCut(VK_DELETE, []));
|
AddItem(MEdit, 'Удалить выделенное', @MenuDelete, ShortCut(VK_DELETE, []));
|
||||||
AddItem(MEdit, 'Выделить всё', @MenuSelectAll, ShortCut(VK_A, [ssCtrl]));
|
AddItem(MEdit, 'Выделить всё', @MenuSelectAll, ShortCut(VK_A, [ssCtrl]));
|
||||||
|
{ Пункт держит и подпись режима: по одному значку на кнопке не угадать,
|
||||||
|
что сейчас делает протяжка мышью. }
|
||||||
|
FSelectModeItem := AddItem(MEdit, 'Режим выделения', @MenuSelectMode,
|
||||||
|
ShortCut(VK_D, []));
|
||||||
AddItem(MEdit, 'Цветовой ключ клипа...', @MenuColorKey);
|
AddItem(MEdit, 'Цветовой ключ клипа...', @MenuColorKey);
|
||||||
AddSep(MEdit);
|
AddSep(MEdit);
|
||||||
AddItem(MEdit, 'Копировать клипы', @MenuCopyClips, ShortCut(VK_C, [ssCtrl]));
|
AddItem(MEdit, 'Копировать клипы', @MenuCopyClips, ShortCut(VK_C, [ssCtrl]));
|
||||||
|
|
@ -900,7 +912,9 @@ procedure TMainForm.BuildJobIndicator;
|
||||||
const
|
const
|
||||||
JobWidth = 330;
|
JobWidth = 330;
|
||||||
LogButtonWidth = 28;
|
LogButtonWidth = 28;
|
||||||
LogButtonLeft = 448;
|
{ Журнал стоит после переключателя выделения: сперва то, что меняет
|
||||||
|
работу с монтажом, потом то, что показывает сообщения. }
|
||||||
|
LogButtonLeft = 478;
|
||||||
begin
|
begin
|
||||||
FJobLabel := TLabel.Create(Self);
|
FJobLabel := TLabel.Create(Self);
|
||||||
FJobLabel.Parent := pnlBottom;
|
FJobLabel.Parent := pnlBottom;
|
||||||
|
|
@ -2575,6 +2589,60 @@ begin
|
||||||
UpdateGlueButton;
|
UpdateGlueButton;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Кнопка режима выделения.
|
||||||
|
|
||||||
|
Режима два, и кнопка показывает не «включено-выключено», а какой сейчас:
|
||||||
|
значок рамки — выделение клипов, значок промежутка — выделение по
|
||||||
|
времени. Поэтому она не утапливается, а меняет картинку. }
|
||||||
|
procedure TMainForm.UpdateSelectModeButton;
|
||||||
|
begin
|
||||||
|
if FSelectModeItem <> nil then
|
||||||
|
if FTimeline.SelectionMode = smRange then
|
||||||
|
FSelectModeItem.Caption := 'Режим выделения: по времени (сейчас)'
|
||||||
|
else
|
||||||
|
FSelectModeItem.Caption := 'Режим выделения: рамкой по клипам (сейчас)';
|
||||||
|
|
||||||
|
if FTimeline.SelectionMode = smRange then
|
||||||
|
begin
|
||||||
|
btnSelectMode.Icon := tiSelectRange;
|
||||||
|
btnSelectMode.Hint := 'Выделение по времени: протяжка отмечает ' +
|
||||||
|
'промежуток по всем дорожкам.' + LineEnding +
|
||||||
|
'S режет все дорожки по обоим краям промежутка.' + LineEnding +
|
||||||
|
'Переключить (D) на выделение клипов рамкой.';
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
btnSelectMode.Icon := tiSelectClips;
|
||||||
|
btnSelectMode.Hint := 'Выделение клипов: рамка выделяет всё, что ' +
|
||||||
|
'задела.' + LineEnding +
|
||||||
|
'S режет по курсору.' + LineEnding +
|
||||||
|
'Переключить (D) на выделение промежутка по времени.';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.btnSelectModeClick(Sender: TObject);
|
||||||
|
begin
|
||||||
|
FTimeline.ToggleSelectionMode;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TMainForm.MenuSelectMode(Sender: TObject);
|
||||||
|
begin
|
||||||
|
FTimeline.ToggleSelectionMode;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Режим сменили — сказать об этом словами: по одному значку не угадать,
|
||||||
|
что теперь делает протяжка мышью. }
|
||||||
|
procedure TMainForm.TimelineSelectModeChange(Sender: TObject);
|
||||||
|
begin
|
||||||
|
UpdateSelectModeButton;
|
||||||
|
if FTimeline.SelectionMode = smRange then
|
||||||
|
SetStatus('Выделение по времени: протяжка отмечает промежуток, ' +
|
||||||
|
'S режет по его краям.', True)
|
||||||
|
else
|
||||||
|
SetStatus('Выделение клипов: рамка выделяет всё, что задела, ' +
|
||||||
|
'S режет по курсору.', True);
|
||||||
|
end;
|
||||||
|
|
||||||
{ Держать ли курсор посередине при воспроизведении.
|
{ Держать ли курсор посередине при воспроизведении.
|
||||||
|
|
||||||
Включено — монтаж едет под курсором, и всегда видно, что будет дальше.
|
Включено — монтаж едет под курсором, и всегда видно, что будет дальше.
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,9 @@ type
|
||||||
tiRipple, // схлопывание: дыра между кусками закрывается
|
tiRipple, // схлопывание: дыра между кусками закрывается
|
||||||
tiLog, // журнал: полоса с сообщениями
|
tiLog, // журнал: полоса с сообщениями
|
||||||
tiGlue, // курсор липнет к границам клипов
|
tiGlue, // курсор липнет к границам клипов
|
||||||
tiTarget // курсор держится середины монтажа
|
tiTarget, // курсор держится середины монтажа
|
||||||
|
tiSelectClips, // выделение рамкой: что задело, то и выделено
|
||||||
|
tiSelectRange // выделение по времени: от и до, по всем дорожкам
|
||||||
);
|
);
|
||||||
|
|
||||||
{ Полоска выполнения, которая рисует себя сама.
|
{ Полоска выполнения, которая рисует себя сама.
|
||||||
|
|
@ -460,6 +462,10 @@ begin
|
||||||
tiLog: Stream.WriteBuffer(IconLogData[0], Length(IconLogData));
|
tiLog: Stream.WriteBuffer(IconLogData[0], Length(IconLogData));
|
||||||
tiGlue: Stream.WriteBuffer(IconGlueData[0], Length(IconGlueData));
|
tiGlue: Stream.WriteBuffer(IconGlueData[0], Length(IconGlueData));
|
||||||
tiTarget: Stream.WriteBuffer(IconTargetData[0], Length(IconTargetData));
|
tiTarget: Stream.WriteBuffer(IconTargetData[0], Length(IconTargetData));
|
||||||
|
tiSelectClips: Stream.WriteBuffer(IconSelectClipsData[0],
|
||||||
|
Length(IconSelectClipsData));
|
||||||
|
tiSelectRange: Stream.WriteBuffer(IconSelectRangeData[0],
|
||||||
|
Length(IconSelectRangeData));
|
||||||
else
|
else
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,16 @@ type
|
||||||
taRubberBand // обтягиваем рамкой клипы по пустому месту
|
taRubberBand // обтягиваем рамкой клипы по пустому месту
|
||||||
);
|
);
|
||||||
|
|
||||||
|
{ Как понимать протяжку мышью по пустому месту.
|
||||||
|
|
||||||
|
smClips — что рамка задела, то и выделено: привычное выделение
|
||||||
|
прямоугольником.
|
||||||
|
|
||||||
|
smRange — отмечается промежуток времени по всем дорожкам сразу, от и
|
||||||
|
до. Нужен, чтобы вырезать кусок фильма целиком: отметил промежуток,
|
||||||
|
нажал S — и все дорожки разрезаны в двух местах, по краям промежутка. }
|
||||||
|
TSelectionMode = (smClips, smRange);
|
||||||
|
|
||||||
{ Что находится под указателем в полосе заголовков дорожек. }
|
{ Что находится под указателем в полосе заголовков дорожек. }
|
||||||
THeaderPart = (hpNone, hpBody, hpMute, hpSolo, hpUp, hpDown, hpName);
|
THeaderPart = (hpNone, hpBody, hpMute, hpSolo, hpUp, hpDown, hpName);
|
||||||
|
|
||||||
|
|
@ -139,6 +149,14 @@ type
|
||||||
FBandToTime: Double;
|
FBandToTime: Double;
|
||||||
FBandFromY: Integer;
|
FBandFromY: Integer;
|
||||||
FBandToY: Integer;
|
FBandToY: Integer;
|
||||||
|
{ Режим выделения и отмеченный им промежуток времени.
|
||||||
|
|
||||||
|
Промежуток живёт до следующего выделения или щелчка: по нему режет
|
||||||
|
клавиша S, и человек должен видеть, где именно это произойдёт. }
|
||||||
|
FSelectionMode: TSelectionMode;
|
||||||
|
FRangeFrom: Double;
|
||||||
|
FRangeTo: Double;
|
||||||
|
FOnSelectionModeChange: TNotifyEvent;
|
||||||
FGroupClips: array of TClip;
|
FGroupClips: array of TClip;
|
||||||
FGroupOrigStarts: array of Double;
|
FGroupOrigStarts: array of Double;
|
||||||
|
|
||||||
|
|
@ -251,6 +269,9 @@ type
|
||||||
const ATime, ATolerance: Double; out AFrom, ATo: Double): Boolean;
|
const ATime, ATolerance: Double; out AFrom, ATo: Double): Boolean;
|
||||||
{ Допуск прилипания в секундах: те же экранные точки, что у краёв. }
|
{ Допуск прилипания в секундах: те же экранные точки, что у краёв. }
|
||||||
function SnapTolerance: Double;
|
function SnapTolerance: Double;
|
||||||
|
procedure SetSelectionMode(AValue: TSelectionMode);
|
||||||
|
{ Рез в указанном месте. Возвращает, сколько клипов разрезано. }
|
||||||
|
function SplitAt(const ATime: Double; APrecise: Boolean): Integer;
|
||||||
protected
|
protected
|
||||||
procedure Paint; override;
|
procedure Paint; override;
|
||||||
procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState;
|
procedure DragOver(Source: TObject; X, Y: Integer; State: TDragState;
|
||||||
|
|
@ -332,6 +353,22 @@ type
|
||||||
procedure SelectInRange(const AFrom, ATo: Double;
|
procedure SelectInRange(const AFrom, ATo: Double;
|
||||||
ATrackFrom, ATrackTo: Integer; AAdd: Boolean);
|
ATrackFrom, ATrackTo: Integer; AAdd: Boolean);
|
||||||
|
|
||||||
|
{ Режим выделения: рамкой по клипам или промежутком времени по всем
|
||||||
|
дорожкам. Переключается клавишей D и кнопкой внизу. }
|
||||||
|
property SelectionMode: TSelectionMode read FSelectionMode
|
||||||
|
write SetSelectionMode;
|
||||||
|
procedure ToggleSelectionMode;
|
||||||
|
{ Отмечен ли промежуток времени и какой. }
|
||||||
|
function HasSelectionRange: Boolean;
|
||||||
|
property RangeFrom: Double read FRangeFrom;
|
||||||
|
property RangeTo: Double read FRangeTo;
|
||||||
|
{ Отметить промежуток. Мышь зовёт это же, и проверки тоже: так
|
||||||
|
поведение реза проверяется числами, без беготни указателем. }
|
||||||
|
procedure SetSelectionRange(const AFrom, ATo: Double);
|
||||||
|
procedure ClearSelectionRange;
|
||||||
|
property OnSelectionModeChange: TNotifyEvent read FOnSelectionModeChange
|
||||||
|
write FOnSelectionModeChange;
|
||||||
|
|
||||||
{ Номер дорожки под указателем мыши, минус один если ни одной. }
|
{ Номер дорожки под указателем мыши, минус один если ни одной. }
|
||||||
property HoverTrack: Integer read FHoverTrack;
|
property HoverTrack: Integer read FHoverTrack;
|
||||||
{ Дорожка, выбранная щелчком по заголовку. }
|
{ Дорожка, выбранная щелчком по заголовку. }
|
||||||
|
|
@ -1381,14 +1418,45 @@ end;
|
||||||
procedure TTimelineControl.DrawRubberBand(ACanvas: TCanvas);
|
procedure TTimelineControl.DrawRubberBand(ACanvas: TCanvas);
|
||||||
var
|
var
|
||||||
L, R, T, B: Integer;
|
L, R, T, B: Integer;
|
||||||
|
Pulling: Boolean;
|
||||||
begin
|
begin
|
||||||
if (FAction <> taRubberBand) or (not FMovedWhileDown) then
|
{ Имя не Dragging: так называется метод самого элемента управления. }
|
||||||
|
Pulling := (FAction = taRubberBand) and FMovedWhileDown;
|
||||||
|
|
||||||
|
{ Отмеченный промежуток виден и после протяжки: по нему режет клавиша S,
|
||||||
|
и человек должен знать, где это произойдёт. }
|
||||||
|
if (not Pulling) and HasSelectionRange then
|
||||||
|
begin
|
||||||
|
L := Max(TimeToX(FRangeFrom), FHeaderWidth);
|
||||||
|
R := Min(TimeToX(FRangeTo), ClientWidth);
|
||||||
|
if R <= L then
|
||||||
|
Exit;
|
||||||
|
ACanvas.Pen.Color := ColorBand;
|
||||||
|
ACanvas.Pen.Style := psDot;
|
||||||
|
ACanvas.Brush.Style := bsClear;
|
||||||
|
ACanvas.Rectangle(L, FRulerHeight, R, ClientHeight);
|
||||||
|
ACanvas.Pen.Style := psSolid;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not Pulling then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
L := Min(TimeToX(FBandFromTime), TimeToX(FBandToTime));
|
L := Min(TimeToX(FBandFromTime), TimeToX(FBandToTime));
|
||||||
R := Max(TimeToX(FBandFromTime), TimeToX(FBandToTime));
|
R := Max(TimeToX(FBandFromTime), TimeToX(FBandToTime));
|
||||||
T := Min(FBandFromY, FBandToY) - FScrollY;
|
|
||||||
B := Max(FBandFromY, FBandToY) - FScrollY;
|
if FSelectionMode = smRange then
|
||||||
|
begin
|
||||||
|
{ По времени отмечают промежуток, а не прямоугольник: рамка идёт во
|
||||||
|
всю высоту дорожек, сколько бы их ни было. }
|
||||||
|
T := FRulerHeight;
|
||||||
|
B := ClientHeight;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
T := Min(FBandFromY, FBandToY) - FScrollY;
|
||||||
|
B := Max(FBandFromY, FBandToY) - FScrollY;
|
||||||
|
end;
|
||||||
|
|
||||||
L := Max(L, FHeaderWidth);
|
L := Max(L, FHeaderWidth);
|
||||||
R := Min(R, ClientWidth);
|
R := Min(R, ClientWidth);
|
||||||
|
|
@ -2618,10 +2686,22 @@ begin
|
||||||
begin
|
begin
|
||||||
FAction := taNone;
|
FAction := taNone;
|
||||||
if FMovedWhileDown then
|
if FMovedWhileDown then
|
||||||
SelectInRange(FBandFromTime, FBandToTime,
|
begin
|
||||||
(FBandFromY - FRulerHeight) div FTrackHeight,
|
if FSelectionMode = smRange then
|
||||||
(FBandToY - FRulerHeight) div FTrackHeight,
|
begin
|
||||||
ssCtrl in Shift)
|
{ Промежуток времени: по всем дорожкам, от и до. Края кладутся на
|
||||||
|
кадровую сетку — по ним потом режет клавиша S. }
|
||||||
|
SetSelectionRange(FBandFromTime, FBandToTime);
|
||||||
|
if (FProject <> nil) and (FRangeTo > FRangeFrom) then
|
||||||
|
SelectInRange(FRangeFrom, FRangeTo, 0, FProject.TrackCount - 1,
|
||||||
|
ssCtrl in Shift);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
SelectInRange(FBandFromTime, FBandToTime,
|
||||||
|
(FBandFromY - FRulerHeight) div FTrackHeight,
|
||||||
|
(FBandToY - FRulerHeight) div FTrackHeight,
|
||||||
|
ssCtrl in Shift);
|
||||||
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
if (FProject <> nil) and not (ssCtrl in Shift) then
|
if (FProject <> nil) and not (ssCtrl in Shift) then
|
||||||
|
|
@ -2630,6 +2710,8 @@ begin
|
||||||
if Assigned(FOnSelectionChange) then
|
if Assigned(FOnSelectionChange) then
|
||||||
FOnSelectionChange(Self);
|
FOnSelectionChange(Self);
|
||||||
end;
|
end;
|
||||||
|
{ Щелчок снимает и отметку промежутка: она про прошлое выделение. }
|
||||||
|
ClearSelectionRange;
|
||||||
Playhead := Max(0, SnapPlayhead(XToTime(X)));
|
Playhead := Max(0, SnapPlayhead(XToTime(X)));
|
||||||
end;
|
end;
|
||||||
FMovedWhileDown := False;
|
FMovedWhileDown := False;
|
||||||
|
|
@ -2720,12 +2802,18 @@ end;
|
||||||
|
|
||||||
{ --- Правки --- }
|
{ --- Правки --- }
|
||||||
|
|
||||||
function TTimelineControl.SplitAtPlayhead(APrecise: Boolean): Integer;
|
{ Рез в указанном месте.
|
||||||
|
|
||||||
|
Место кладётся на кадровую сетку: между кадрами времени нет, и рез на
|
||||||
|
доле кадра оставил бы в монтаже время, которого не бывает. Сдвиг не
|
||||||
|
больше полкадра. }
|
||||||
|
function TTimelineControl.SplitAt(const ATime: Double;
|
||||||
|
APrecise: Boolean): Integer;
|
||||||
var
|
var
|
||||||
T: Integer;
|
T: Integer;
|
||||||
Track: TTrack;
|
Track: TTrack;
|
||||||
Clip, Tail: TClip;
|
Clip, Tail: TClip;
|
||||||
Offset: Double;
|
Offset, At: Double;
|
||||||
{ Старый номер связки — новый. Одна связка при резе становится двумя:
|
{ Старый номер связки — новый. Одна связка при резе становится двумя:
|
||||||
голова со своей парой и хвост со своей. }
|
голова со своей парой и хвост со своей. }
|
||||||
NewGroups: TStringList;
|
NewGroups: TStringList;
|
||||||
|
|
@ -2735,6 +2823,7 @@ begin
|
||||||
if FProject = nil then
|
if FProject = nil then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
|
At := QuantizeTime(ATime);
|
||||||
FProject.BeginChange;
|
FProject.BeginChange;
|
||||||
NewGroups := TStringList.Create;
|
NewGroups := TStringList.Create;
|
||||||
try
|
try
|
||||||
|
|
@ -2745,11 +2834,11 @@ begin
|
||||||
for T := 0 to FProject.TrackCount - 1 do
|
for T := 0 to FProject.TrackCount - 1 do
|
||||||
begin
|
begin
|
||||||
Track := FProject.Tracks[T];
|
Track := FProject.Tracks[T];
|
||||||
Clip := Track.ClipAt(FPlayhead);
|
Clip := Track.ClipAt(At);
|
||||||
if Clip = nil then
|
if Clip = nil then
|
||||||
Continue;
|
Continue;
|
||||||
|
|
||||||
Offset := FPlayhead - Clip.TlStart;
|
Offset := At - Clip.TlStart;
|
||||||
{ Рез вплотную к краю ничего не даёт, только плодит клипы нулевой длины. }
|
{ Рез вплотную к краю ничего не даёт, только плодит клипы нулевой длины. }
|
||||||
if (Offset < 0.001) or (Offset > Clip.Duration - 0.001) then
|
if (Offset < 0.001) or (Offset > Clip.Duration - 0.001) then
|
||||||
Continue;
|
Continue;
|
||||||
|
|
@ -2758,7 +2847,7 @@ begin
|
||||||
Tail.Id := Clip.Id + '_b';
|
Tail.Id := Clip.Id + '_b';
|
||||||
Tail.MediaId := Clip.MediaId;
|
Tail.MediaId := Clip.MediaId;
|
||||||
Tail.SrcIn := Clip.SrcIn + Offset;
|
Tail.SrcIn := Clip.SrcIn + Offset;
|
||||||
Tail.TlStart := FPlayhead;
|
Tail.TlStart := At;
|
||||||
Tail.Duration := Clip.Duration - Offset;
|
Tail.Duration := Clip.Duration - Offset;
|
||||||
Tail.EdgeOut := Clip.EdgeOut;
|
Tail.EdgeOut := Clip.EdgeOut;
|
||||||
{ Хвост — тот же клип, только вторая его половина. Всё настроенное
|
{ Хвост — тот же клип, только вторая его половина. Всё настроенное
|
||||||
|
|
@ -2819,6 +2908,90 @@ begin
|
||||||
FProject.Undo;
|
FProject.Undo;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Режим выделения: рамкой по клипам или промежутком по времени.
|
||||||
|
|
||||||
|
Смена режима снимает отмеченный промежуток: он принадлежит режиму «по
|
||||||
|
времени», и оставлять его в другом режиме значило бы резать по невидимой
|
||||||
|
метке. }
|
||||||
|
procedure TTimelineControl.SetSelectionMode(AValue: TSelectionMode);
|
||||||
|
begin
|
||||||
|
if FSelectionMode = AValue then
|
||||||
|
Exit;
|
||||||
|
FSelectionMode := AValue;
|
||||||
|
ClearSelectionRange;
|
||||||
|
Invalidate;
|
||||||
|
if Assigned(FOnSelectionModeChange) then
|
||||||
|
FOnSelectionModeChange(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTimelineControl.ToggleSelectionMode;
|
||||||
|
begin
|
||||||
|
if FSelectionMode = smClips then
|
||||||
|
SelectionMode := smRange
|
||||||
|
else
|
||||||
|
SelectionMode := smClips;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TTimelineControl.HasSelectionRange: Boolean;
|
||||||
|
begin
|
||||||
|
Result := (FSelectionMode = smRange) and (FRangeTo - FRangeFrom > 0.0005);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTimelineControl.SetSelectionRange(const AFrom, ATo: Double);
|
||||||
|
begin
|
||||||
|
FRangeFrom := Max(0, QuantizeTime(Min(AFrom, ATo)));
|
||||||
|
FRangeTo := Max(0, QuantizeTime(Max(AFrom, ATo)));
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTimelineControl.ClearSelectionRange;
|
||||||
|
begin
|
||||||
|
if (FRangeFrom = 0) and (FRangeTo = 0) then
|
||||||
|
Exit;
|
||||||
|
FRangeFrom := 0;
|
||||||
|
FRangeTo := 0;
|
||||||
|
Invalidate;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Рез по команде человека.
|
||||||
|
|
||||||
|
Обычно — по курсору. Если отмечен промежуток времени (режим выделения
|
||||||
|
«по времени»), режем по обоим его краям: это и есть «вырезать кусок
|
||||||
|
фильма» — два реза, между ними то, что уйдёт.
|
||||||
|
|
||||||
|
Курсор после реза встаёт ровно на место реза: дальше с этим местом и
|
||||||
|
работают — удаляют кусок, схлопывают дыру, ставят следующий рез. }
|
||||||
|
function TTimelineControl.SplitAtPlayhead(APrecise: Boolean): Integer;
|
||||||
|
var
|
||||||
|
At: Double;
|
||||||
|
begin
|
||||||
|
Result := 0;
|
||||||
|
if FProject = nil then
|
||||||
|
Exit;
|
||||||
|
|
||||||
|
if HasSelectionRange then
|
||||||
|
begin
|
||||||
|
{ Сперва дальний край: рез не двигает то, что левее, но порядок
|
||||||
|
всё равно понятнее от конца к началу. }
|
||||||
|
Result := SplitAt(FRangeTo, APrecise);
|
||||||
|
Result := Result + SplitAt(FRangeFrom, APrecise);
|
||||||
|
Playhead := Max(0, QuantizeTime(FRangeFrom));
|
||||||
|
|
||||||
|
{ После реза выделенной остаётся ровно середина — то, что между
|
||||||
|
краями. Иначе Delete убрал бы не её: выделение осталось бы на
|
||||||
|
клипах, какими они были до реза, а середина оказалась бы новой,
|
||||||
|
невыделенной половиной. }
|
||||||
|
if Result > 0 then
|
||||||
|
SelectInRange(FRangeFrom, FRangeTo, 0, FProject.TrackCount - 1, False);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
At := QuantizeTime(FPlayhead);
|
||||||
|
Result := SplitAt(At, APrecise);
|
||||||
|
if Result > 0 then
|
||||||
|
Playhead := Max(0, At);
|
||||||
|
end;
|
||||||
|
|
||||||
{ Расширяет выделение на всю связку: клипы, связанные с уже выделенными,
|
{ Расширяет выделение на всю связку: клипы, связанные с уже выделенными,
|
||||||
тоже считаются выделенными. Иначе удаление или перенос разорвали бы
|
тоже считаются выделенными. Иначе удаление или перенос разорвали бы
|
||||||
связку пополам. }
|
связку пополам. }
|
||||||
|
|
@ -2974,7 +3147,7 @@ var
|
||||||
T, C: Integer;
|
T, C: Integer;
|
||||||
Track: TTrack;
|
Track: TTrack;
|
||||||
AnyChange: Boolean;
|
AnyChange: Boolean;
|
||||||
HoleEnd: Double;
|
HoleEnd, HoleStart: Double;
|
||||||
Touched: TStringList;
|
Touched: TStringList;
|
||||||
begin
|
begin
|
||||||
if FProject = nil then
|
if FProject = nil then
|
||||||
|
|
@ -2986,6 +3159,7 @@ begin
|
||||||
|
|
||||||
AnyChange := False;
|
AnyChange := False;
|
||||||
HoleEnd := 0;
|
HoleEnd := 0;
|
||||||
|
HoleStart := -1;
|
||||||
FProject.BeginChange;
|
FProject.BeginChange;
|
||||||
Touched := TStringList.Create;
|
Touched := TStringList.Create;
|
||||||
try
|
try
|
||||||
|
|
@ -2997,6 +3171,9 @@ begin
|
||||||
begin
|
begin
|
||||||
{ Правый край дыры: дальше него всё поедет влево. }
|
{ Правый край дыры: дальше него всё поедет влево. }
|
||||||
HoleEnd := Max(HoleEnd, Track[C].TlEnd);
|
HoleEnd := Max(HoleEnd, Track[C].TlEnd);
|
||||||
|
{ Левый край — место будущего стыка: туда встанет курсор. }
|
||||||
|
if (HoleStart < 0) or (Track[C].TlStart < HoleStart) then
|
||||||
|
HoleStart := Track[C].TlStart;
|
||||||
if Touched.IndexOf(IntToStr(T)) < 0 then
|
if Touched.IndexOf(IntToStr(T)) < 0 then
|
||||||
Touched.Add(IntToStr(T));
|
Touched.Add(IntToStr(T));
|
||||||
Track.Remove(Track[C]);
|
Track.Remove(Track[C]);
|
||||||
|
|
@ -3014,7 +3191,17 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if AnyChange then
|
if AnyChange then
|
||||||
StructureChanged
|
begin
|
||||||
|
{ Курсор — на место, где был вырезанный кусок: при схлопывании там
|
||||||
|
теперь стык, без схлопывания — начало дыры. С этим местом и работают
|
||||||
|
дальше, и искать его мышью заново незачем. }
|
||||||
|
if HoleStart >= 0 then
|
||||||
|
Playhead := Max(0, HoleStart);
|
||||||
|
{ Отметка промежутка пережила бы удаление и резала бы потом по
|
||||||
|
пустому месту. }
|
||||||
|
ClearSelectionRange;
|
||||||
|
StructureChanged;
|
||||||
|
end
|
||||||
else
|
else
|
||||||
FProject.Undo;
|
FProject.Undo;
|
||||||
end;
|
end;
|
||||||
|
|
@ -3095,6 +3282,9 @@ begin
|
||||||
|
|
||||||
if AnyChange then
|
if AnyChange then
|
||||||
begin
|
begin
|
||||||
|
{ Курсор — на стык: дыры больше нет, а работать дальше будут именно с
|
||||||
|
этим местом. }
|
||||||
|
Playhead := Max(0, GapStart);
|
||||||
StructureChanged;
|
StructureChanged;
|
||||||
Result := True;
|
Result := True;
|
||||||
end
|
end
|
||||||
|
|
@ -3120,7 +3310,7 @@ procedure TTimelineControl.SelectInRange(const AFrom, ATo: Double;
|
||||||
ATrackFrom, ATrackTo: Integer; AAdd: Boolean);
|
ATrackFrom, ATrackTo: Integer; AAdd: Boolean);
|
||||||
var
|
var
|
||||||
T, C, T1, T2: Integer;
|
T, C, T1, T2: Integer;
|
||||||
From_, To_: Double;
|
From_, To_, Eps: Double;
|
||||||
Clip: TClip;
|
Clip: TClip;
|
||||||
begin
|
begin
|
||||||
if FProject = nil then
|
if FProject = nil then
|
||||||
|
|
@ -3129,6 +3319,14 @@ begin
|
||||||
{ Рамку тянут в любую сторону — приводим её к возрастающему виду. }
|
{ Рамку тянут в любую сторону — приводим её к возрастающему виду. }
|
||||||
From_ := Min(AFrom, ATo);
|
From_ := Min(AFrom, ATo);
|
||||||
To_ := Max(AFrom, ATo);
|
To_ := Max(AFrom, ATo);
|
||||||
|
|
||||||
|
{ Нужно настоящее перекрытие, а не касание краями. Сосед, кончающийся
|
||||||
|
ровно там, где рамка начинается, в неё не попал — но сложение долей
|
||||||
|
даёт разницу в последнем знаке, и «больше» срабатывает. Порог —
|
||||||
|
полкадра: перекрытия короче кадра не бывает. }
|
||||||
|
Eps := 0.0005;
|
||||||
|
if (FProject.Master.FpsNum > 0) and (FProject.Master.FpsDen > 0) then
|
||||||
|
Eps := FProject.Master.FpsDen / FProject.Master.FpsNum / 2;
|
||||||
T1 := Max(0, Min(ATrackFrom, ATrackTo));
|
T1 := Max(0, Min(ATrackFrom, ATrackTo));
|
||||||
T2 := Min(FProject.TrackCount - 1, Max(ATrackFrom, ATrackTo));
|
T2 := Min(FProject.TrackCount - 1, Max(ATrackFrom, ATrackTo));
|
||||||
|
|
||||||
|
|
@ -3139,7 +3337,7 @@ begin
|
||||||
for C := 0 to FProject.Tracks[T].Count - 1 do
|
for C := 0 to FProject.Tracks[T].Count - 1 do
|
||||||
begin
|
begin
|
||||||
Clip := FProject.Tracks[T][C];
|
Clip := FProject.Tracks[T][C];
|
||||||
if (Clip.TlEnd > From_) and (Clip.TlStart < To_) then
|
if (Clip.TlEnd > From_ + Eps) and (Clip.TlStart < To_ - Eps) then
|
||||||
Clip.Selected := True;
|
Clip.Selected := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -3189,6 +3387,7 @@ begin
|
||||||
SelectAll;
|
SelectAll;
|
||||||
Key := 0;
|
Key := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
VK_HOME:
|
VK_HOME:
|
||||||
begin
|
begin
|
||||||
{ К началу самого раннего клипа, а не просто в ноль: если монтаж
|
{ К началу самого раннего клипа, а не просто в ноль: если монтаж
|
||||||
|
|
|
||||||
|
|
@ -631,6 +631,128 @@ begin
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Рез и курсор: где режет S и куда после этого встаёт курсор.
|
||||||
|
|
||||||
|
Раньше курсор оставался там, где был, а рез мог попасть на долю кадра.
|
||||||
|
Теперь рез кладётся на кадровую сетку, а курсор встаёт ровно на место
|
||||||
|
реза — с ним и работают дальше. }
|
||||||
|
procedure CheckSplitAndPlayhead;
|
||||||
|
var
|
||||||
|
Proj: TProject;
|
||||||
|
Info: TMediaInfo;
|
||||||
|
Media: TMediaItem;
|
||||||
|
Timeline: TTimelineControl;
|
||||||
|
Step: Double;
|
||||||
|
I, N: Integer;
|
||||||
|
begin
|
||||||
|
WriteLn;
|
||||||
|
WriteLn('Рез, промежуток и курсор');
|
||||||
|
WriteLn;
|
||||||
|
|
||||||
|
Info := TestInfo;
|
||||||
|
Info.FpsNum := 60000;
|
||||||
|
Info.FpsDen := 1001;
|
||||||
|
Step := Info.FpsDen / Info.FpsNum;
|
||||||
|
|
||||||
|
Proj := TProject.Create;
|
||||||
|
Timeline := TTimelineControl.Create(nil);
|
||||||
|
try
|
||||||
|
Proj.Clear;
|
||||||
|
Proj.AddTrack('V1');
|
||||||
|
Proj.AddTrack('A1');
|
||||||
|
Media := Proj.AddMedia('/несуществующий/видео.mp4', Info);
|
||||||
|
Proj.Master := Info;
|
||||||
|
{ По два клипа на каждой дорожке, встык: 0..10 и 10..20. }
|
||||||
|
Proj.AddClip(Proj.Tracks[0], Media, 0, 10, 0);
|
||||||
|
Proj.AddClip(Proj.Tracks[0], Media, 20, 10, 10);
|
||||||
|
Proj.AddClip(Proj.Tracks[1], Media, 0, 10, 0);
|
||||||
|
Proj.AddClip(Proj.Tracks[1], Media, 20, 10, 10);
|
||||||
|
|
||||||
|
Timeline.Project := Proj;
|
||||||
|
Timeline.PixelsPerSecond := 100;
|
||||||
|
|
||||||
|
{ Рез по курсору: место кладётся на кадр, курсор встаёт на него. }
|
||||||
|
Timeline.Playhead := 3.3177;
|
||||||
|
CheckTrue('разрезаны обе дорожки', Timeline.SplitAtPlayhead(False) = 2);
|
||||||
|
CheckTrue('курсор встал на место реза',
|
||||||
|
Abs(Timeline.Playhead - Round(3.3177 / Step) * Step) < 0.0001);
|
||||||
|
CheckTrue('курсор на кадровой сетке',
|
||||||
|
Abs(Timeline.Playhead / Step - Round(Timeline.Playhead / Step)) < 0.001);
|
||||||
|
N := 0;
|
||||||
|
for I := 0 to Proj.Tracks[0].Count - 1 do
|
||||||
|
if Abs(Proj.Tracks[0][I].TlStart - Timeline.Playhead) < 0.0001 then
|
||||||
|
Inc(N);
|
||||||
|
CheckTrue('рез пришёлся ровно туда, где курсор', N = 1);
|
||||||
|
|
||||||
|
{ Промежуток по времени: рез по обоим краям, курсор — на начало. }
|
||||||
|
Timeline.SelectionMode := smRange;
|
||||||
|
Timeline.SetSelectionRange(12.13, 16.77);
|
||||||
|
CheckTrue('промежуток отмечен', Timeline.HasSelectionRange);
|
||||||
|
CheckTrue('разрезано по два клипа на дорожке',
|
||||||
|
Timeline.SplitAtPlayhead(False) = 4);
|
||||||
|
CheckTrue('курсор — на начале промежутка',
|
||||||
|
Abs(Timeline.Playhead - Timeline.RangeFrom) < 0.0001);
|
||||||
|
|
||||||
|
N := 0;
|
||||||
|
for I := 0 to Proj.Tracks[1].Count - 1 do
|
||||||
|
if (Abs(Proj.Tracks[1][I].TlStart - Timeline.RangeFrom) < 0.0001) or
|
||||||
|
(Abs(Proj.Tracks[1][I].TlStart - Timeline.RangeTo) < 0.0001) then
|
||||||
|
Inc(N);
|
||||||
|
CheckTrue('на звуковой дорожке резы по обоим краям', N = 2);
|
||||||
|
|
||||||
|
{ После реза выделена середина — то, что между краями: следующим
|
||||||
|
действием её обычно удаляют. }
|
||||||
|
N := 0;
|
||||||
|
for I := 0 to Proj.Tracks[0].Count - 1 do
|
||||||
|
if Proj.Tracks[0][I].Selected then
|
||||||
|
begin
|
||||||
|
Inc(N);
|
||||||
|
CheckTrue('выделенное лежит внутри промежутка',
|
||||||
|
(Proj.Tracks[0][I].TlStart >= Timeline.RangeFrom - 0.0001) and
|
||||||
|
(Proj.Tracks[0][I].TlEnd <= Timeline.RangeTo + 0.0001));
|
||||||
|
end;
|
||||||
|
CheckTrue('на дорожке картинки выделена одна середина', N = 1);
|
||||||
|
|
||||||
|
{ Смена режима снимает промежуток: резать по невидимой метке нельзя. }
|
||||||
|
Timeline.SelectionMode := smClips;
|
||||||
|
CheckTrue('смена режима сняла промежуток',
|
||||||
|
not Timeline.HasSelectionRange);
|
||||||
|
|
||||||
|
{ Удаление со схлопыванием: курсор встаёт на место стыка. }
|
||||||
|
Proj.Clear;
|
||||||
|
Proj.AddTrack('V1');
|
||||||
|
Media := Proj.AddMedia('/несуществующий/видео.mp4', Info);
|
||||||
|
Proj.Master := Info;
|
||||||
|
Proj.RippleEnabled := True;
|
||||||
|
Proj.AddClip(Proj.Tracks[0], Media, 0, 5, 0);
|
||||||
|
Proj.AddClip(Proj.Tracks[0], Media, 10, 5, 5);
|
||||||
|
Proj.AddClip(Proj.Tracks[0], Media, 20, 5, 10);
|
||||||
|
Timeline.Playhead := 0;
|
||||||
|
Proj.Tracks[0][1].Selected := True;
|
||||||
|
Timeline.DeleteSelected;
|
||||||
|
CheckTrue('после удаления клипов стало два', Proj.Tracks[0].Count = 2);
|
||||||
|
CheckTrue('курсор — на месте вырезанного куска',
|
||||||
|
Abs(Timeline.Playhead - 5) < 0.0001);
|
||||||
|
CheckTrue('хвост придвинут к стыку',
|
||||||
|
Abs(Proj.Tracks[0][1].TlStart - 5) < 0.0001);
|
||||||
|
|
||||||
|
{ Схлопывание дыры с клавиатуры: курсор тоже на стык. }
|
||||||
|
Proj.Clear;
|
||||||
|
Proj.AddTrack('V1');
|
||||||
|
Media := Proj.AddMedia('/несуществующий/видео.mp4', Info);
|
||||||
|
Proj.Master := Info;
|
||||||
|
Proj.AddClip(Proj.Tracks[0], Media, 0, 5, 0);
|
||||||
|
Proj.AddClip(Proj.Tracks[0], Media, 20, 5, 12);
|
||||||
|
Timeline.ActiveTrack := 0;
|
||||||
|
Timeline.Playhead := 8;
|
||||||
|
CheckTrue('дыра схлопнута', Timeline.RippleCloseGap);
|
||||||
|
CheckTrue('курсор — на стыке', Abs(Timeline.Playhead - 5) < 0.0001);
|
||||||
|
finally
|
||||||
|
Timeline.Free;
|
||||||
|
Proj.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
WriteLn('Свойства из файлов форм');
|
WriteLn('Свойства из файлов форм');
|
||||||
WriteLn;
|
WriteLn;
|
||||||
|
|
@ -663,6 +785,7 @@ begin
|
||||||
CheckClipCopy;
|
CheckClipCopy;
|
||||||
CheckRubberBand;
|
CheckRubberBand;
|
||||||
CheckFrameGridRepair;
|
CheckFrameGridRepair;
|
||||||
|
CheckSplitAndPlayhead;
|
||||||
CheckSplitAllTracks;
|
CheckSplitAllTracks;
|
||||||
CheckGapTolerance;
|
CheckGapTolerance;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ ICONS = [
|
||||||
('Log', 'icons8-property-30.png', 24),
|
('Log', 'icons8-property-30.png', 24),
|
||||||
('Glue', 'icons8-клей-30.png', 24),
|
('Glue', 'icons8-клей-30.png', 24),
|
||||||
('Target', 'icons8-цель-30.png', 24),
|
('Target', 'icons8-цель-30.png', 24),
|
||||||
|
('SelectClips', 'icons8-select-all-90.png', 24),
|
||||||
|
('SelectRange', 'icons8-width-90.png', 24),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Значок самой программы: отдельно, потому что окну нужен формат ico —
|
# Значок самой программы: отдельно, потому что окну нужен формат ico —
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue