[pitivi] timeline: Fix flickering when dragging a thin layer over a thick layer
- From: Thibault Saunier <tsaunier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] timeline: Fix flickering when dragging a thin layer over a thick layer
- Date: Fri, 23 Oct 2015 17:10:38 +0000 (UTC)
commit 144f47d82aa15252c7168ccadd96b038c8145008
Author: Alexandru Băluț <alexandru balut gmail com>
Date: Mon Oct 5 00:19:58 2015 +0200
timeline: Fix flickering when dragging a thin layer over a thick layer
Now there is a parameter called past_middle_when_adjacent to
__getLayerAt. If y is exactly on the layer above or below the preferred
layer, normally we'd return that layer/position.
Consider there are thin layers and thick layers.
If past_middle_when_adjacent is True, y must be far enough so that if the
layers would be switched, y will be on preferred_layer. Otherwise if the
layers are switched but y is still on the same layer, flickering takes
place.
Differential Revision: https://phabricator.freedesktop.org/D327
pitivi/timeline/timeline.py | 28 ++++++---
tests/Makefile.am | 1 +
tests/test_timeline_timeline.py | 128 +++++++++++++++++++++++++++++++++++++++
3 files changed, 147 insertions(+), 10 deletions(-)
---
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index b754d86..8504185 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -616,7 +616,8 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
elif self.__moving_layer:
event_widget = self.get_event_widget(event)
unused_x, y = event_widget.translate_coordinates(self, event.x, event.y)
- layer, unused_on_sep = self.__getLayerAt(y, prefer_bLayer=self.__moving_layer)
+ layer, unused_on_sep = self.__getLayerAt(
+ y, prefer_bLayer=self.__moving_layer, past_middle_when_adjacent=True)
if layer != self.__moving_layer:
priority = layer.get_priority()
self.moveLayer(self.__moving_layer, priority)
@@ -819,10 +820,8 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
layer = layers.pop(bLayer.get_priority())
layers.insert(index, layer)
- i = 0
- for layer in layers:
+ for i, layer in enumerate(layers):
layer.set_priority(i)
- i += 1
def _addLayer(self, bLayer):
control = LayerControls(bLayer, self.app)
@@ -852,9 +851,8 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
def __resetLayersByPriority(self, reset=False):
self._layers.sort(key=lambda layer: layer.bLayer.props.priority)
- i = 0
self.debug("Reseting layers priorities")
- for layer in self._layers:
+ for i, layer in enumerate(self._layers):
if reset:
layer.bLayer.props.priority = i
@@ -866,8 +864,6 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
"position",
layer.bLayer.props.priority)
- i += 1
-
def _removeLayer(self, bLayer):
self.info("Removing layer: %s", bLayer.props.priority)
self.__layers_vbox.remove(bLayer.ui.get_parent())
@@ -918,7 +914,7 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
return [getattr(bLayer.ui, sep_name), getattr(bLayer.control_ui, sep_name)]
- def __getLayerAt(self, y, prefer_bLayer=None):
+ def __getLayerAt(self, y, prefer_bLayer=None, past_middle_when_adjacent=False):
bLayers = self.bTimeline.get_layers()
if y < 20:
# The cursor is at the top, above the first layer.
@@ -932,12 +928,24 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
# as possible when having an option (y is between two layers).
prefer_after = True
+ if past_middle_when_adjacent:
+ index_preferred = prefer_bLayer.get_priority()
+ height_preferred = prefer_bLayer.ui.get_allocation().height
+
for i, bLayer in enumerate(bLayers):
layer_rect = bLayer.ui.get_allocation()
layer_y = layer_rect.y
layer_height = layer_rect.height
if layer_y <= y < layer_y + layer_height:
- # The cursor is on a layer.
+ # The cursor is exactly on bLayer.
+ if past_middle_when_adjacent:
+ # Check if far enough from prefer_bLayer.
+ delta = index_preferred - bLayer.get_priority()
+ if (delta == 1 and y >= layer_y + height_preferred) or \
+ (delta == -1 and y < layer_y + layer_height - height_preferred):
+ # bLayer is adjacent to prefer_bLayer, but the cursor
+ # is not far enough to warrant a change.
+ return prefer_bLayer, []
return bLayer, []
separators = self.__layerGetSeps(bLayer, "after_sep")
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 180ffd9..d1d972e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -15,6 +15,7 @@ tests = \
test_project.py \
test_projectsettings.py \
test_system.py \
+ test_timeline_timeline.py \
test_undo.py \
test_undo_timeline.py \
test_utils.py \
diff --git a/tests/test_timeline_timeline.py b/tests/test_timeline_timeline.py
new file mode 100644
index 0000000..b31d8b7
--- /dev/null
+++ b/tests/test_timeline_timeline.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2015, Alex Băluț <alexandru balut gmail com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+import mock
+from unittest import TestCase
+
+from gi.repository import Gdk
+
+from pitivi.project import Project, ProjectManager
+from pitivi.timeline.timeline import Timeline
+from pitivi.utils import ui
+
+
+SEPARATOR_HEIGHT = 4
+THIN = ui.LAYER_HEIGHT / 2
+THICK = ui.LAYER_HEIGHT
+
+
+class TestLayers(TestCase):
+
+ def createTimeline(self, layers_heights):
+ project_manager = ProjectManager(app=None)
+ project_manager.newBlankProject()
+ project = project_manager.current_project
+ timeline = Timeline(container=None, app=None)
+ timeline.get_parent = mock.MagicMock()
+ timeline.setProject(project)
+ y = 0
+ for priority, height in enumerate(layers_heights):
+ bLayer = timeline.createLayer(priority=priority)
+ rect = Gdk.Rectangle()
+ rect.y = y
+ rect.height = height
+ bLayer.ui.set_allocation(rect)
+ y += height + SEPARATOR_HEIGHT
+ return timeline
+
+ def testDraggingLayer(self):
+ self.checkGetLayerAt([THIN, THIN, THIN], 1, True,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2])
+ self.checkGetLayerAt([THICK, THICK, THICK], 1, True,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2])
+ self.checkGetLayerAt([THIN, THICK, THIN], 1, True,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2])
+ self.checkGetLayerAt([THICK, THIN, THICK], 1, True,
+ [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2])
+
+ def testDraggingClipFromLayer(self):
+ self.checkGetLayerAt([THIN, THIN, THIN], 1, False,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2])
+ self.checkGetLayerAt([THICK, THICK, THICK], 1, False,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2])
+ self.checkGetLayerAt([THIN, THICK, THIN], 1, False,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2])
+ self.checkGetLayerAt([THICK, THIN, THICK], 1, False,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2])
+
+ def testDraggingClipFromOuterSpace(self):
+ self.checkGetLayerAt([THIN, THIN, THIN], None, False,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2])
+ self.checkGetLayerAt([THICK, THICK, THICK], None, False,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2])
+ self.checkGetLayerAt([THIN, THICK, THIN], None, False,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2])
+ self.checkGetLayerAt([THICK, THIN, THICK], None, False,
+ [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2])
+
+ def checkGetLayerAt(self, heights, preferred, past_middle_when_adjacent, expectations):
+ timeline = self.createTimeline(heights)
+ bLayers = [layer.bLayer for layer in timeline._layers]
+ if preferred is None:
+ preferred_bLayer = None
+ else:
+ preferred_bLayer = bLayers[preferred]
+ h = [layer.get_allocation().height for layer in timeline._layers]
+ s = SEPARATOR_HEIGHT
+
+ def assertLayerAt(bLayer, y):
+ result = timeline._Timeline__getLayerAt(
+ int(y),
+ prefer_bLayer=preferred_bLayer,
+ past_middle_when_adjacent=past_middle_when_adjacent)
+ self.assertEqual(
+ bLayer,
+ result[0],
+ "Expected %d, got %d at %d" % (bLayers.index(bLayer), bLayers.index(result[0]), y))
+
+ # y on the top layer.
+ assertLayerAt(bLayers[expectations[0]], 0)
+ assertLayerAt(bLayers[expectations[1]], h[0] / 2 - 1)
+ assertLayerAt(bLayers[expectations[2]], h[0] / 2)
+ assertLayerAt(bLayers[expectations[3]], h[0] - 1)
+
+ # y on the separator.
+ assertLayerAt(bLayers[expectations[4]], h[0])
+ assertLayerAt(bLayers[expectations[5]], h[0] + s - 1)
+
+ # y on the middle layer.
+ assertLayerAt(bLayers[expectations[6]], h[0] + s)
+ assertLayerAt(bLayers[expectations[7]], h[0] + s + h[1] / 2 - 1)
+ assertLayerAt(bLayers[expectations[8]], h[0] + s + h[1] / 2)
+ assertLayerAt(bLayers[expectations[9]], h[0] + s + h[1] - 1)
+
+ # y on the separator.
+ assertLayerAt(bLayers[expectations[10]], h[0] + s + h[1])
+ assertLayerAt(bLayers[expectations[11]], h[0] + s + h[1] + s - 1)
+
+ # y on the bottom layer.
+ assertLayerAt(bLayers[expectations[12]], h[0] + s + h[1] + s)
+ assertLayerAt(bLayers[expectations[13]], h[0] + s + h[1] + s + h[2] / 2 - 1)
+ assertLayerAt(bLayers[expectations[14]], h[0] + s + h[1] + s + h[2] / 2)
+ assertLayerAt(bLayers[expectations[15]], h[0] + s + h[1] + s + h[2] - 1)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]