Skip to content

Commit ee8e501

Browse files
committed
Playhead snapping support, when SHIFT is pressed. Improved timeline styling: Removed left+right borders on clips/transitions to fix invalid width and jitter while trimming/moving items. Added brightness to selected clips, and flipped gradient on tracks (to make clips pop more).
1 parent 246d63e commit ee8e501

File tree

8 files changed

+58
-39
lines changed

8 files changed

+58
-39
lines changed

src/timeline/js/controllers.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,9 @@ App.controller("TimelineCtrl", function ($scope) {
11391139
// Add playhead position to array
11401140
var playhead_pixel_position = $scope.project.playhead_position * $scope.pixelsPerSecond;
11411141
var playhead_diff = position - playhead_pixel_position;
1142-
diffs.push({"diff": playhead_diff, "position": playhead_pixel_position});
1142+
if (!ignore_ids.hasOwnProperty("playhead")) {
1143+
diffs.push({"diff": playhead_diff, "position": playhead_pixel_position});
1144+
}
11431145

11441146
// Loop through diffs (and find the smallest one)
11451147
for (var diff_index = 0; diff_index < diffs.length; diff_index++) {

src/timeline/js/directives/clip.js

+3-10
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,7 @@ App.directive("tlClip", function ($timeout) {
131131

132132
//apply the new start, end and length to the clip's scope
133133
scope.$apply(function () {
134-
// Get the nearest starting frame position to the clip position (this helps to prevent cutting
135-
// in-between frames, and thus less likely to repeat or skip a frame).
136-
new_position = (Math.round((new_position * scope.project.fps.num) / scope.project.fps.den) * scope.project.fps.den ) / scope.project.fps.num;
137-
new_right = (Math.round((new_right * scope.project.fps.num) / scope.project.fps.den) * scope.project.fps.den ) / scope.project.fps.num;
138-
new_left = (Math.round((new_left * scope.project.fps.num) / scope.project.fps.den) * scope.project.fps.den ) / scope.project.fps.num;
139-
134+
// Apply clip scope changes
140135
if (scope.clip.end !== new_right) {
141136
scope.clip.end = new_right;
142137
}
@@ -191,13 +186,12 @@ App.directive("tlClip", function ($timeout) {
191186
// changing the end of the clips
192187
new_right -= delta_time;
193188
if (new_right > scope.clip.duration) {
194-
195189
// change back to actual duration (for the preview below)
196190
new_right = scope.clip.duration;
197191
ui.element.width(new_right * scope.pixelsPerSecond);
198192
}
199193
else {
200-
ui.element.width(ui.size.width);
194+
ui.element.width((new_right - new_left) * scope.pixelsPerSecond);
201195
}
202196
}
203197

@@ -260,8 +254,7 @@ App.directive("tlClip", function ($timeout) {
260254
}
261255

262256
// Apply scope up to this point
263-
scope.$apply(function () {
264-
});
257+
scope.$apply(function () {});
265258

266259
var scrolling_tracks = $("#scrolling_tracks");
267260
var vert_scroll_offset = scrolling_tracks.scrollTop();

src/timeline/js/directives/playhead.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,30 @@ App.directive("tlPlayhead", function () {
3737
// get the default top position so we can lock it in place vertically
3838
playhead_y_max = element.position().top;
3939

40+
element.on("mousedown", function (e) {
41+
// Set bounding box for the playhead
42+
bounding_box = {};
43+
setBoundingBox(scope, $(this), true);
44+
});
45+
4046
// Move playhead to new position (if it's not currently being animated)
4147
element.on("mousemove", function (e) {
4248
if (e.which === 1 && !scope.playhead_animating) { // left button
43-
var playhead_seconds = (e.pageX - $("#ruler").offset().left) / scope.pixelsPerSecond;
49+
// Calculate the playhead bounding box movement and apply snapping rules
50+
var cursor_position = e.pageX - $("#ruler").offset().left;
51+
var results = moveBoundingBox(scope, bounding_box.left, bounding_box.top,
52+
cursor_position - bounding_box.left, cursor_position - bounding_box.top,
53+
cursor_position, cursor_position, true);
54+
55+
// Only apply snapping when SHIFT is pressed
56+
if (e.shiftKey) {
57+
new_position = results.position.left;
58+
} else {
59+
new_position = cursor_position;
60+
}
61+
62+
// Move playhead
63+
var playhead_seconds = new_position / scope.pixelsPerSecond;
4464
scope.movePlayhead(playhead_seconds);
4565
scope.previewFrame(playhead_seconds);
4666
}

src/timeline/js/directives/transition.js

-6
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,6 @@ App.directive("tlTransition", function () {
108108

109109
//apply the new start, end and length to the transition's scope
110110
scope.$apply(function () {
111-
112-
// Get the nearest starting frame position to the transition position (this helps to prevent cutting
113-
// in-between frames, and thus less likely to repeat or skip a frame).
114-
new_right = (Math.round((new_right * scope.project.fps.num) / scope.project.fps.den) * scope.project.fps.den ) / scope.project.fps.num;
115-
new_left = (Math.round((new_left * scope.project.fps.num) / scope.project.fps.den) * scope.project.fps.den ) / scope.project.fps.num;
116-
117111
if (dragLoc === "right") {
118112
scope.transition.end = new_right;
119113
}

src/timeline/js/functions.js

+22-12
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ function hasLockedTrack(scope, top, bottom) {
220220
var bounding_box = Object();
221221

222222
// Build bounding box (since multiple items can be selected)
223-
function setBoundingBox(scope, item) {
223+
function setBoundingBox(scope, item, is_playhead=false) {
224224
var scrolling_tracks = $("#scrolling_tracks");
225225
var vert_scroll_offset = scrolling_tracks.scrollTop();
226226
var horz_scroll_offset = scrolling_tracks.scrollLeft();
@@ -237,6 +237,11 @@ function setBoundingBox(scope, item) {
237237
bounding_box.right = item_right;
238238
bounding_box.height = item.height();
239239
bounding_box.width = item.width();
240+
if (is_playhead) {
241+
bounding_box.left += 13; // center
242+
bounding_box.right = bounding_box.left;
243+
bounding_box.width = 1;
244+
}
240245
} else {
241246
//compare and change if item is a better fit for bounding box edges
242247
if (item_top < bounding_box.top) { bounding_box.top = item_top; }
@@ -252,28 +257,33 @@ function setBoundingBox(scope, item) {
252257
}
253258

254259
// Get list of current selected ids (so we can ignore their snapping x coordinates)
260+
// Unless playhead, where is don't want to ignore any selected clips
255261
bounding_box.selected_ids = {};
256-
for (var clip_index = 0; clip_index < scope.project.clips.length; clip_index++) {
257-
if (scope.project.clips[clip_index].selected) {
258-
bounding_box.selected_ids[scope.project.clips[clip_index].id] = true;
262+
if (!is_playhead) {
263+
for (var clip_index = 0; clip_index < scope.project.clips.length; clip_index++) {
264+
if (scope.project.clips[clip_index].selected) {
265+
bounding_box.selected_ids[scope.project.clips[clip_index].id] = true;
266+
}
259267
}
260-
}
261-
for (var effect_index = 0; effect_index < scope.project.effects.length; effect_index++) {
262-
if (scope.project.effects[effect_index].selected) {
263-
bounding_box.selected_ids[scope.project.effects[effect_index].id] = true;
268+
for (var effect_index = 0; effect_index < scope.project.effects.length; effect_index++) {
269+
if (scope.project.effects[effect_index].selected) {
270+
bounding_box.selected_ids[scope.project.effects[effect_index].id] = true;
271+
}
264272
}
273+
} else {
274+
bounding_box.selected_ids["playhead"] = true;
265275
}
266276
}
267277

268278
// Move bounding box (apply snapping and constraints)
269-
function moveBoundingBox(scope, previous_x, previous_y, x_offset, y_offset, left, top) {
279+
function moveBoundingBox(scope, previous_x, previous_y, x_offset, y_offset, left, top, is_playhead=false) {
270280
// Store result of snapping logic (left, top)
271281
var snapping_result = Object();
272282
snapping_result.left = left;
273283
snapping_result.top = top;
274284

275285
// Check for shift key
276-
if (event.shiftKey) {
286+
if (!is_playhead && event.shiftKey) {
277287
// freeze X movement
278288
x_offset = 0;
279289
snapping_result.left = previous_x;
@@ -309,8 +319,8 @@ function moveBoundingBox(scope, previous_x, previous_y, x_offset, y_offset, left
309319
}
310320

311321
// Find closest nearby object, if any (for snapping)
312-
var bounding_box_padding = 3; // not sure why this is needed, but it helps line everything up
313-
var results = scope.getNearbyPosition([bounding_box.left, bounding_box.right + bounding_box_padding], 10.0, bounding_box.selected_ids);
322+
var results = scope.getNearbyPosition([bounding_box.left, bounding_box.right],
323+
10.0, bounding_box.selected_ids);
314324
var nearby_offset = results[0];
315325
var snapline_position = results[1];
316326

src/timeline/media/css/main.css

+7-7
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ img {
5858
.track_menu:hover { background-image: url(../images/menu_hover.png); }
5959
.track_lock { float: left; background-image: url(../images/lock.png); width:14px;height:14px; margin-top: 2px; }
6060
.track_label { padding-top: 2px; padding-left: 2px; padding-left: 4px; text-shadow: 0 0 10px #ffffff; }
61-
.track_name { width: 140px; height: 64px; color: #fff; font-size: 9pt; margin-left: 5px; margin-bottom: 8px; background-color: #000; border: 1px solid #4B92AD; border-top-left-radius: 8px; border-bottom-left-radius: 8px; box-shadow: 0px 0px 10px #000; }
61+
.track_name { width: 140px; height: 62px; color: #fff; font-size: 9pt; margin-left: 5px; margin-bottom: 8px; background-color: #000; border: 1px solid #4B92AD; border-top-left-radius: 8px; border-bottom-left-radius: 8px; box-shadow: 0px 0px 10px #000; }
6262
.track_disabled { background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(20,20,20,1)), color-stop(100%,rgba(6,6,6,1))) !important; }
63-
.track { height: 64px; background-color: #000; margin-bottom: 8px; background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(50,50,50,1)), color-stop(100%,rgba(6,6,6,1))); border-top: 1px solid #4b92ad; border-bottom: 1px solid #4b92ad; border-right: 1px solid #4B92AD; border-top-right-radius: 8px; border-bottom-right-radius: 8px; box-shadow: 0px 0px 10px #000; }
63+
.track { height: 62px; background-color: #000; margin-bottom: 8px; background: -webkit-gradient(linear, left bottom, left top, color-stop(0%,rgba(50,50,50,1)), color-stop(100%,rgba(6,6,6,1))); border-top: 1px solid #4b92ad; border-bottom: 1px solid #4b92ad; border-right: 1px solid #4B92AD; border-top-right-radius: 8px; border-bottom-right-radius: 8px; box-shadow: 0px 0px 10px #000; }
6464

6565
/* Playhead */
66-
.playhead-line { z-index: 9998; position: absolute; height:316px; top:0px; width:1px; background-color:#ff0024; opacity:1;}
66+
.playhead-line { z-index: 9999; position: absolute; height:316px; top:0px; width:1px; background-color:#ff0024; opacity:1;}
6767
.playhead-line-small { z-index: 9999; position: absolute; height:20px; top:32px;; margin-left: 12px; width:1px; background-color:#ff0024; opacity:1;}
6868
.playhead-top { cursor:move; z-index: 9999; position: absolute; margin-left: -12px; margin-top: 12px; width:25px; height:32px; background-image: url(../images/play_head.png); opacity:1;}
6969
.marker {position:absolute; top:30px;}
@@ -79,12 +79,12 @@ img {
7979
.effect_icon {margin-left:1px;margin-right:1px;}
8080
.razor_cursor { cursor: url(../images/razor_line_with_razor.png), default!important; }
8181

82-
.clip { cursor: move; z-index:1000; color: #fff; min-width:0px; height: 64px; position: absolute; margin-top:0px; left: 100px; font-size: 9pt; background-color: #ef7f00; background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(82,82,82,1)), color-stop(100%,rgba(34,38,40,1))); border-radius: 8px; border: 1px solid #4b92ad; border-left: 2px solid #4b92ad; border-right: 2px solid #4b92ad; box-shadow: 0px 0px 10px #000; opacity: 0.95; box-sizing: border-box;}
82+
.clip { cursor: move; z-index:1000; color: #fff; min-width:0px; height: 64px; position: absolute; margin-top:0px; left: 100px; font-size: 9pt; background-color: #ef7f00; background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(82,82,82,1)), color-stop(100%,rgba(34,38,40,1))); border-radius: 8px; border: 1px solid #4b92ad; border-left: 0px solid #4b92ad; border-right: 0px solid #4b92ad; box-shadow: 0px 0px 10px #000; opacity: 0.95; box-sizing: border-box;}
8383
.left_edge_stop { border-left: 2px solid #4b92ad; }
8484
.right_edge_stop { border-right: 2px solid #4b92ad; }
8585
.highlight_clip { border-color: #32b7ea; }
86-
.ui-selecting { border: 1px solid yellow!important; border-left: 2px solid yellow!important; border-right: 2px solid yellow!important; box-sizing: border-box; }
87-
.ui-selected { border: 1px solid red!important; border-left: 2px solid red!important; border-right: 2px solid red!important; box-sizing: border-box; }
86+
.ui-selecting { border: 1px solid yellow!important; border-left: 0px solid yellow!important; border-right: 0px solid yellow!important; box-sizing: border-box; }
87+
.ui-selected { filter: brightness(1.3); border: 1px solid red!important; border-left: 0px solid red!important; border-right: 0px solid red!important; box-sizing: border-box; }
8888
.thumb { margin-left: 5px; width: 66px; height: 38px;}
8989
.thumb-start { float:left; }
9090
.thumb-end {float:right; }
@@ -152,7 +152,7 @@ img {
152152
}
153153

154154
/* Snapping / snapline */
155-
.snapping-line { z-index: 9999; position: absolute; height:316px; top:0px; width:1px; background-color:#32b7ea; opacity:0.75; -webkit-transition: 0.3s linear all; }
155+
.snapping-line { z-index: 9998; position: absolute; height:316px; top:0px; width:1px; background-color:#32b7ea; opacity:0.75; }
156156

157157
/* Snap line Animations */
158158
.snapping-line.ng-hide-add { display: block !important; }

src/windows/models/properties_model.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def update_frame(self, frame_number, reload_model=True):
160160
if self.frame_number > max_frame_number:
161161
self.frame_number = max_frame_number
162162

163-
log.info("Update frame to %s" % self.frame_number)
163+
log.debug("Update frame to %s" % self.frame_number)
164164

165165
# Update the model data
166166
if reload_model:

src/windows/preview_thread.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def previewFrame(self, number):
199199
# Mark frame number for processing
200200
self.Seek(number)
201201

202-
log.info(
202+
log.debug(
203203
"previewFrame: %s, player Position(): %s",
204204
number, self.player.Position())
205205

0 commit comments

Comments
 (0)