[pitivi] layer: Toggle layer visibility



commit 9168854962b7f61c94ad5bd0d5d8c1ff56d695e3
Author: Vivek R <123vivekr gmail com>
Date:   Sun Apr 5 22:52:23 2020 +0530

    layer: Toggle layer visibility
    
    Closes #2422

 data/pixmaps/eye-not-looking-symbolic.svg          |  3 +
 data/pixmaps/eye-open-negative-filled-symbolic.svg | 65 +++++++++++++++++++
 pitivi/timeline/layer.py                           | 70 +++++++++++++++-----
 tests/test_timeline_layer.py                       | 75 ++++++++++++++++++----
 4 files changed, 185 insertions(+), 28 deletions(-)
---
diff --git a/data/pixmaps/eye-not-looking-symbolic.svg b/data/pixmaps/eye-not-looking-symbolic.svg
new file mode 100644
index 00000000..ad75f422
--- /dev/null
+++ b/data/pixmaps/eye-not-looking-symbolic.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg"; width="16" height="16">
+    <path d="M13.98 1.99a1 1 0 0 0-.687.303l-.984.984A8 8 0 0 0 8 2 8 8 0 0 0 .262 8.01a8 8 0 0 0 2.943 
4.37l-.912.913a1 1 0 1 0 1.414 1.414l11-11a1 1 0 0 0-.727-1.717zM8 4a4 4 0 0 1 2.611.974l-1.42 1.42A2 2 0 0 0 
8 6a2 2 0 0 0-2 2 2 2 0 0 0 .396 1.19l-1.42 1.42A4 4 0 0 1 4 8a4 4 0 0 1 4-4zm7.03 2.209l-3.344 3.343a4 4 0 0 
1-2.127 2.127l-2.28 2.28a8 8 0 0 0 .721.04 8 8 0 0 0 7.738-6.01 8 8 0 0 0-.709-1.78zm-7.53.79a.5.5 0 0 1 
.5.5.5.5 0 0 1-.5.5.5.5 0 0 1-.5-.5.5.5 0 0 1 .5-.5z" fill="#2e3436"/>
+</svg>
diff --git a/data/pixmaps/eye-open-negative-filled-symbolic.svg 
b/data/pixmaps/eye-open-negative-filled-symbolic.svg
new file mode 100644
index 00000000..dc8b5d00
--- /dev/null
+++ b/data/pixmaps/eye-open-negative-filled-symbolic.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb";
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   width="16"
+   viewBox="0 0 16 16"
+   version="1.1"
+   id="svg7384"
+   height="16">
+  <metadata
+     id="metadata90">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title>Gnome Symbolic Icon Theme</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <title
+     id="title9167">Gnome Symbolic Icon Theme</title>
+  <defs
+     id="defs7386">
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7212">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop7214" />
+    </linearGradient>
+  </defs>
+  <g
+     transform="translate(-341.0002,-13.000323)"
+     style="display:inline"
+     id="layer9" />
+  <g
+     transform="translate(-100,-380.00032)"
+     id="layer1" />
+  <g
+     transform="translate(-100,-380.00032)"
+     style="display:inline"
+     id="layer10">
+    <path
+       d="m 108,382 a 8,8 0 0 0 -7.73828,6.00977 A 8,8 0 0 0 108,394 8,8 0 0 0 115.73828,387.99023 8,8 0 0 0 
108,382 Z m 0,2 a 4,4 0 0 1 4,4 4,4 0 0 1 -4,4 4,4 0 0 1 -4,-4 4,4 0 0 1 4,-4 z"
+       id="path2314"
+       
style="opacity:1;vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal"
 />
+    <path
+       id="path2318"
+       d="m 110,388.00003 a 2,2 0 0 1 -2,2 2,2 0 0 1 -2,-2 2,2 0 0 1 2,-2 2,2 0 0 1 2,2 z"
+       
style="vector-effect:none;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
 />
+  </g>
+  <g
+     transform="translate(-100,-380.00032)"
+     id="g6387" />
+  <g
+     transform="translate(-100,-380.00032)"
+     id="layer11" />
+</svg>
diff --git a/pitivi/timeline/layer.py b/pitivi/timeline/layer.py
index d8e2658e..2e5b2124 100644
--- a/pitivi/timeline/layer.py
+++ b/pitivi/timeline/layer.py
@@ -31,6 +31,17 @@ from pitivi.utils.ui import PADDING
 from pitivi.utils.ui import SEPARATOR_HEIGHT
 
 
+AUDIO_ICONS = {
+    True: "audio-volume-high-symbolic",
+    False: "audio-volume-muted-symbolic",
+}
+
+VIDEO_ICONS = {
+    True: "eye-open-negative-filled-symbolic",
+    False: "eye-not-looking-symbolic",
+}
+
+
 class SpacedSeparator(Gtk.EventBox):
     """A Separator with vertical spacing.
 
@@ -60,6 +71,7 @@ class LayerControls(Gtk.EventBox, Loggable):
 
         tracks = self.ges_timeline.get_tracks()
         self.timeline_audio_tracks = [track for track in tracks if track.props.track_type == 
GES.TrackType.AUDIO]
+        self.timeline_video_tracks = [track for track in tracks if track.props.track_type == 
GES.TrackType.VIDEO]
 
         hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
         self.add(hbox)
@@ -86,12 +98,19 @@ class LayerControls(Gtk.EventBox, Loggable):
         self.__update_name()
         name_row.pack_start(self.name_entry, True, True, 0)
 
-        self.mute_toggle_button = Gtk.ToggleButton.new()
-        self.mute_toggle_button.props.valign = Gtk.Align.CENTER
-        self.mute_toggle_button.props.relief = Gtk.ReliefStyle.NONE
-        self.mute_toggle_button.connect("toggled", self.__mute_button_toggled_cb)
-        self.__update_mute_button()
-        name_row.pack_start(self.mute_toggle_button, False, False, 0)
+        self.audio_button = Gtk.Button.new()
+        self.audio_button.connect("clicked", self.__audio_button_clicked_cb)
+        self.__update_audio_button()
+
+        self.video_button = Gtk.Button.new()
+        self.video_button.connect("clicked", self.__video_button_clicked_cb)
+        self.__update_video_button()
+
+        control_box = Gtk.ButtonBox()
+        control_box.set_layout(Gtk.ButtonBoxStyle.EXPAND)
+        control_box.add(self.video_button)
+        control_box.add(self.audio_button)
+        name_row.pack_start(control_box, False, False, 0)
 
         self.menubutton = Gtk.MenuButton.new()
         self.menubutton.props.valign = Gtk.Align.CENTER
@@ -224,24 +243,43 @@ class LayerControls(Gtk.EventBox, Loggable):
         self.ges_timeline.ui.move_layer(self.ges_layer, index)
         self.app.project_manager.current_project.pipeline.commit_timeline()
 
-    def __mute_button_toggled_cb(self, button):
-        self.ges_layer.set_active_for_tracks(not button.get_active(), self.timeline_audio_tracks)
+    def __audio_button_clicked_cb(self, button):
+        self.ges_layer.set_active_for_tracks(not self.__check_tracks_active(
+            self.timeline_audio_tracks), self.timeline_audio_tracks)
         self.app.project_manager.current_project.pipeline.commit_timeline()
 
-    def __update_mute_button(self):
-        muted = all([not self.ges_layer.get_active_for_track(t) for t in self.timeline_audio_tracks])
-        self.mute_toggle_button.set_active(muted)
-        icon_name = "audio-volume-muted-symbolic" if muted else "audio-volume-high-symbolic"
-        image = Gtk.Image.new_from_icon_name(icon_name, Gtk.IconSize.BUTTON)
-        self.mute_toggle_button.set_image(image)
+    def __update_audio_button(self):
+        active = self.__check_tracks_active(self.timeline_audio_tracks)
+        icon = AUDIO_ICONS[active]
+        self.audio_button.set_image(Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON))
+
+    def __video_button_clicked_cb(self, button):
+        self.ges_layer.set_active_for_tracks(not self.__check_tracks_active(
+            self.timeline_video_tracks), self.timeline_video_tracks)
+        self.app.project_manager.current_project.pipeline.commit_timeline()
+
+    def __update_video_button(self):
+        active = self.__check_tracks_active(self.timeline_video_tracks)
+        icon = VIDEO_ICONS[active]
+        self.video_button.set_image(Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON))
 
     def __layer_active_changed_cb(self, ges_layer, active, tracks):
-        self.__update_mute_button()
+        self.__update_video_button()
+        self.__update_audio_button()
+
+    def __check_tracks_active(self, tracks):
+        return all([self.ges_layer.get_active_for_track(t) for t in tracks])
 
     def update(self, media_types):
         self.props.height_request = self.ges_layer.ui.props.height_request
 
-        if media_types & GES.TrackType.VIDEO or not media_types:
+        has_audio = media_types & GES.TrackType.AUDIO
+        self.audio_button.set_sensitive(has_audio)
+
+        has_video = media_types & GES.TrackType.VIDEO
+        self.video_button.set_sensitive(has_video)
+
+        if has_video or not media_types:
             # The layer has video or is empty.
             icon = "video-x-generic-symbolic"
         else:
diff --git a/tests/test_timeline_layer.py b/tests/test_timeline_layer.py
index fa0bcf24..752ab88f 100644
--- a/tests/test_timeline_layer.py
+++ b/tests/test_timeline_layer.py
@@ -18,7 +18,9 @@ from unittest import mock
 
 from gi.repository import GES
 
+from pitivi.timeline.layer import AUDIO_ICONS
 from pitivi.timeline.layer import Layer
+from pitivi.timeline.layer import VIDEO_ICONS
 from tests import common
 
 
@@ -46,50 +48,99 @@ class TestLayerControl(common.TestCase):
         layer.set_name("Layer 0x")
         self.assertEqual(layer.get_name(), "Layer 0x")
 
-    def test_mute_and_unmute_layer(self):
+    def test_audio_toggle(self):
+        """Checks that audio toggling is reflected in the UI."""
         timeline_container = common.create_timeline_container()
         timeline = timeline_container.timeline
         ges_layer = timeline.ges_timeline.append_layer()
         layer_controls = ges_layer.control_ui
-        mute_toggle_button = layer_controls.mute_toggle_button
+        audio_button = layer_controls.audio_button
 
         for audio_track in layer_controls.timeline_audio_tracks:
             self.assertTrue(ges_layer.get_active_for_track(audio_track))
-            self.assertFalse(mute_toggle_button.get_active())
+            self.assertEqual(audio_button.get_image().props.icon_name, AUDIO_ICONS[True])
 
             ges_layer.set_active_for_tracks(False, [audio_track])
             common.create_main_loop().run(until_empty=True)
             self.assertFalse(ges_layer.get_active_for_track(audio_track))
-            self.assertTrue(mute_toggle_button.get_active())
+            self.assertEqual(audio_button.get_image().props.icon_name, AUDIO_ICONS[False])
 
             ges_layer.set_active_for_tracks(True, [audio_track])
             common.create_main_loop().run(until_empty=True)
             self.assertTrue(ges_layer.get_active_for_track(audio_track))
-            self.assertFalse(mute_toggle_button.get_active())
+            self.assertEqual(audio_button.get_image().props.icon_name, AUDIO_ICONS[True])
 
-    def test_mute_and_unmute_layer_button(self):
+    def test_audio_button(self):
+        """Checks that the audio button toggles the audio track."""
         timeline_container = common.create_timeline_container()
         timeline = timeline_container.timeline
         ges_layer = timeline.ges_timeline.append_layer()
         layer_controls = ges_layer.control_ui
-        mute_toggle_button = layer_controls.mute_toggle_button
+        audio_button = layer_controls.audio_button
 
         for audio_track in layer_controls.timeline_audio_tracks:
             self.assertTrue(ges_layer.get_active_for_track(audio_track))
         common.create_main_loop().run(until_empty=True)
-        self.assertFalse(mute_toggle_button.get_active())
+        self.assertEqual(audio_button.get_image().props.icon_name, AUDIO_ICONS[True])
 
-        mute_toggle_button.clicked()
+        audio_button.clicked()
         for audio_track in layer_controls.timeline_audio_tracks:
             self.assertFalse(ges_layer.get_active_for_track(audio_track))
         common.create_main_loop().run(until_empty=True)
-        self.assertTrue(mute_toggle_button.get_active())
+        self.assertEqual(audio_button.get_image().props.icon_name, AUDIO_ICONS[False])
 
-        mute_toggle_button.clicked()
+        audio_button.clicked()
         for audio_track in layer_controls.timeline_audio_tracks:
             self.assertTrue(ges_layer.get_active_for_track(audio_track))
         common.create_main_loop().run(until_empty=True)
-        self.assertFalse(mute_toggle_button.get_active())
+        self.assertEqual(audio_button.get_image().props.icon_name, AUDIO_ICONS[True])
+
+    def test_video_toggle(self):
+        """Checks that video toggling is reflected in the UI."""
+        timeline_container = common.create_timeline_container()
+        timeline = timeline_container.timeline
+        ges_layer = timeline.ges_timeline.append_layer()
+        layer_controls = ges_layer.control_ui
+        video_button = layer_controls.video_button
+
+        for video_track in layer_controls.timeline_video_tracks:
+            self.assertTrue(ges_layer.get_active_for_track(video_track))
+            self.assertEqual(video_button.get_image().props.icon_name, VIDEO_ICONS[True])
+
+            ges_layer.set_active_for_tracks(False, [video_track])
+            common.create_main_loop().run(until_empty=True)
+            self.assertFalse(ges_layer.get_active_for_track(video_track))
+            self.assertEqual(video_button.get_image().props.icon_name, VIDEO_ICONS[False])
+
+            ges_layer.set_active_for_tracks(True, [video_track])
+            common.create_main_loop().run(until_empty=True)
+            self.assertTrue(ges_layer.get_active_for_track(video_track))
+            self.assertEqual(video_button.get_image().props.icon_name, VIDEO_ICONS[True])
+
+    def test_video_button(self):
+        """Checks that the video button toggles the video track."""
+        timeline_container = common.create_timeline_container()
+        timeline = timeline_container.timeline
+        ges_layer = timeline.ges_timeline.append_layer()
+        layer_controls = ges_layer.control_ui
+        video_button = layer_controls.video_button
+
+        for video_track in layer_controls.timeline_video_tracks:
+            self.assertTrue(ges_layer.get_active_for_track(video_track))
+        common.create_main_loop().run(until_empty=True)
+        self.assertEqual(video_button.get_image().props.icon_name, VIDEO_ICONS[True])
+
+        video_button.clicked()
+        for video_track in layer_controls.timeline_video_tracks:
+            self.assertFalse(ges_layer.get_active_for_track(video_track))
+        common.create_main_loop().run(until_empty=True)
+        self.assertEqual(video_button.get_image().props.icon_name, VIDEO_ICONS[False])
+
+        video_button.clicked()
+        for video_track in layer_controls.timeline_video_tracks:
+            self.assertTrue(ges_layer.get_active_for_track(video_track))
+        common.create_main_loop().run(until_empty=True)
+        self.assertEqual(video_button.get_image().props.icon_name, VIDEO_ICONS[True])
 
 
 class TestLayer(common.TestCase):


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]