[pitivi] viewer: Allows Users to visualize and customize the safe areas for their projects



commit 47ab733dfa99d8ff4501c85b84d83e2d56bb1e30
Author: Evan Palmer <evanp922 gmail com>
Date:   Mon Apr 20 18:02:11 2020 -0500

    viewer: Allows Users to visualize and customize the safe areas for their projects
    
    Currently, Users do not have a method to visually observe the areas of
    their projects which are safe from cropping. This may lead to unwanted
    cropping of their projects when they are broadcasted.
    
    To resolve this issue, implement a new overlay class which is not
    attached to a source to draw the Safe Areas, add a method to resize Safe
    Areas to the project settings, implement a keyboard shortcut to toggle
    the visibility of the overlay, and add documentation to the User Manual
    regarding how to modify the size of a project's Safe Areas and toggle
    the visibility of the overlay.
    
    Closes #2297

 data/ui/projectsettings.ui            | 209 +++++++++++++++++++++++++++++++++-
 help/C/cheatsheet.page                |   8 ++
 help/C/workwithprojects.page          |   8 ++
 pitivi/project.py                     |  79 +++++++++++++
 pitivi/viewer/overlay_stack.py        |   4 +
 pitivi/viewer/safe_areas_overlay.py   |  67 +++++++++++
 pitivi/viewer/viewer.py               |  12 ++
 tests/test_project.py                 |  18 +++
 tests/test_viewer_safeareasoverlay.py |  29 +++++
 tests/test_viewer_viewer.py           |  40 +++++++
 10 files changed, 469 insertions(+), 5 deletions(-)
---
diff --git a/data/ui/projectsettings.ui b/data/ui/projectsettings.ui
index 91c50637..20c83fde 100644
--- a/data/ui/projectsettings.ui
+++ b/data/ui/projectsettings.ui
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.1 -->
+<!-- Generated with glade 3.22.2 -->
 <interface>
   <requires lib="gtk+" version="3.10"/>
   <object class="GtkAdjustment" id="adjustment1">
@@ -35,6 +35,26 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkAdjustment" id="adjustment6">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment7">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment8">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment9">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
   <object class="GtkDialog" id="project-settings-dialog">
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
@@ -105,7 +125,8 @@
                   <object class="GtkAlignment" id="alignment1">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="left_padding">12</property>
+                    <property name="xalign">0</property>
+                    <property name="left_padding">6</property>
                     <child>
                       <object class="GtkBox" id="video_tab">
                         <property name="visible">True</property>
@@ -379,7 +400,7 @@
                   <object class="GtkAlignment" id="alignment2">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="left_padding">12</property>
+                    <property name="left_padding">6</property>
                     <child>
                       <object class="GtkBox" id="audio_tab">
                         <property name="visible">True</property>
@@ -560,7 +581,7 @@
                   <object class="GtkAlignment" id="alignment3">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="left_padding">12</property>
+                    <property name="left_padding">6</property>
                     <child>
                       <object class="GtkGrid" id="info_box">
                         <property name="visible">True</property>
@@ -650,7 +671,7 @@
                   <object class="GtkAlignment" id="alignment4">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="left_padding">12</property>
+                    <property name="left_padding">6</property>
                     <child>
                       <object class="GtkBox" id="video_tab1">
                         <property name="visible">True</property>
@@ -817,6 +838,184 @@
                 <property name="top_attach">2</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkFrame" id="frame5">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment5">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="top_padding">12</property>
+                    <property name="bottom_padding">12</property>
+                    <property name="left_padding">18</property>
+                    <property name="right_padding">12</property>
+                    <child>
+                      <object class="GtkGrid">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="row_spacing">6</property>
+                        <property name="column_spacing">6</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="halign">start</property>
+                            <property name="label" translatable="yes">Title:</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                            <property name="width">4</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="halign">start</property>
+                            <property name="margin_top">6</property>
+                            <property name="label" translatable="yes">Action:</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">2</property>
+                            <property name="width">4</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="action_safe_area_horizontal">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">●</property>
+                            <property name="text" translatable="no">1</property>
+                            <property name="adjustment">adjustment8</property>
+                            <property name="numeric">True</property>
+                            <property name="value">1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="action_safe_area_vertical">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">●</property>
+                            <property name="text" translatable="no">1</property>
+                            <property name="adjustment">adjustment9</property>
+                            <property name="numeric">True</property>
+                            <property name="value">1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">2</property>
+                            <property name="top_attach">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="title_safe_area_horizontal">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">●</property>
+                            <property name="text" translatable="no">1</property>
+                            <property name="adjustment">adjustment6</property>
+                            <property name="numeric">True</property>
+                            <property name="value">1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="title_safe_area_vertical">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">●</property>
+                            <property name="text" translatable="no">1</property>
+                            <property name="adjustment">adjustment7</property>
+                            <property name="numeric">True</property>
+                            <property name="value">1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">2</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label20">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="no">%</property>
+                            <property name="xalign">0</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">3</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label21">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="no">%</property>
+                            <property name="xalign">0</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">3</property>
+                            <property name="top_attach">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="no">×</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label15">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="no">×</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Safe Areas</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
             <child>
               <placeholder/>
             </child>
diff --git a/help/C/cheatsheet.page b/help/C/cheatsheet.page
index 6b67a00b..bac02907 100644
--- a/help/C/cheatsheet.page
+++ b/help/C/cheatsheet.page
@@ -91,4 +91,12 @@
       <!--item><p><key>.</key> and <key>,</key>: Go to the next or previous property keyframe.</p></item-->
     </list>
   </section>
+  <section id="viewer">
+    <title>Viewer</title>
+    <list>
+      <item>
+        <p><key>'</key>: Toggle the safe areas</p>
+      </item>
+    </list>
+  </section>
 </page>
diff --git a/help/C/workwithprojects.page b/help/C/workwithprojects.page
index 5a11dcbe..2b7691d0 100644
--- a/help/C/workwithprojects.page
+++ b/help/C/workwithprojects.page
@@ -44,4 +44,12 @@
       <p>When you import the first video file the project settings are automatically changed to match. See 
<link xref="importing">Getting media</link> for details.</p>
     </note>
   </section>
+  <section id="safeareas">
+    <title>Preventing cropping of essential content on television screens</title>
+    <p>Depending on how a television is built, the video may be cropped when being displayed on its screen. 
The essential content can be placed in the <em>action-safe area</em>, which is a centered rectangular area 
safe from cropping. Similarly, titles can be placed in the smaller <em>title-safe area</em> to prevent the 
text sticking to the screen edges.</p>
+    <p>The safe areas are displayed on the <gui>viewer</gui> as an overlay and will not affect the video of 
a project. The size of the action-safe area and the title-safe area can be specified in the <link 
xref="#projectsettings">Project Settings</link>. For example, a size of 90% vertical × 80% horizontal 
corresponds to a 5% margin at the top and bottom and a 10% horizontal margin at the left and right of the 
video. Producers use their own guidelines since no television standard defines the <link 
href="https://en.wikipedia.org/wiki/Safe_area_(television)">safe areas</link>.</p>
+    <note style="tip">
+      <p>To toggle Safe Areas on and off, press <key>'</key>.</p>
+    </note>
+  </section>
 </page>
diff --git a/pitivi/project.py b/pitivi/project.py
index 24706043..2aa6b698 100644
--- a/pitivi/project.py
+++ b/pitivi/project.py
@@ -91,6 +91,12 @@ IGNORED_PROPS = ["name", "parent"]
 DEFAULT_VIDEO_SETTINGS = "video/x-raw,width=1920,height=1080,framerate=(GstFraction)30/1"
 DEFAULT_AUDIO_SETTINGS = "audio/x-raw,channels=2,rate=48000"
 
+# Default values for the safe areas.
+DEFAULT_TITLE_AREA_VERTICAL = 0.8
+DEFAULT_TITLE_AREA_HORIZONTAL = 0.8
+DEFAULT_ACTION_AREA_VERTICAL = 0.9
+DEFAULT_ACTION_AREA_HORIZONTAL = 0.9
+
 SCALED_THUMB_WIDTH = 96
 SCALED_THUMB_HEIGHT = 54
 SCALED_THUMB_DIR = "96x54"
@@ -644,6 +650,7 @@ class Project(Loggable, GES.Project):
         "settings-set-from-imported-asset": (GObject.SignalFlags.RUN_LAST, None,
                                              (GES.Asset,)),
         "video-size-changed": (GObject.SignalFlags.RUN_LAST, None, ()),
+        "safe-area-size-changed": (GObject.SignalFlags.RUN_LAST, None, ())
     }
 
     def __init__(self, app, uri=None, scenario=None, **unused_kwargs):
@@ -694,6 +701,10 @@ class Project(Loggable, GES.Project):
 
         self.register_meta(GES.MetaFlag.READWRITE, "scaled_proxy_width", 0)
         self.register_meta(GES.MetaFlag.READWRITE, "scaled_proxy_height", 0)
+        self.register_meta(GES.MetaFlag.READWRITE, "pitivi::title_safe_area_vertical", 
DEFAULT_TITLE_AREA_VERTICAL)
+        self.register_meta(GES.MetaFlag.READWRITE, "pitivi::title_safe_area_horizontal", 
DEFAULT_TITLE_AREA_HORIZONTAL)
+        self.register_meta(GES.MetaFlag.READWRITE, "pitivi::action_safe_area_vertical", 
DEFAULT_ACTION_AREA_VERTICAL)
+        self.register_meta(GES.MetaFlag.READWRITE, "pitivi::action_safe_area_horizontal", 
DEFAULT_ACTION_AREA_HORIZONTAL)
 
         # The rendering settings.
         self.set_meta("render-scale", 100.0)
@@ -1131,6 +1142,57 @@ class Project(Loggable, GES.Project):
         if value:
             self.set_meta("render-scale", value)
 
+    def set_safe_areas_sizes(self, title_horizontal_factor, title_vertical_factor, action_horizontal_factor, 
action_vertical_factor):
+        """Sets the safe areas sizes in one operation."""
+        self.title_safe_area_horizontal = title_horizontal_factor
+        self.title_safe_area_vertical = title_vertical_factor
+        self.action_safe_area_horizontal = action_horizontal_factor
+        self.action_safe_area_vertical = action_vertical_factor
+
+    @property
+    def title_safe_area_vertical(self):
+        return self.get_meta("pitivi::title_safe_area_vertical")
+
+    @title_safe_area_vertical.setter
+    def title_safe_area_vertical(self, percentage):
+        if percentage == self.get_meta("pitivi::title_safe_area_vertical"):
+            return
+        self.set_meta("pitivi::title_safe_area_vertical", percentage)
+        self.emit("safe-area-size-changed")
+
+    @property
+    def title_safe_area_horizontal(self):
+        return self.get_meta("pitivi::title_safe_area_horizontal")
+
+    @title_safe_area_horizontal.setter
+    def title_safe_area_horizontal(self, percentage):
+        if percentage == self.get_meta("pitivi::title_safe_area_horizontal"):
+            return
+        self.set_meta("pitivi::title_safe_area_horizontal", percentage)
+        self.emit("safe-area-size-changed")
+
+    @property
+    def action_safe_area_vertical(self):
+        return self.get_meta("pitivi::action_safe_area_vertical")
+
+    @action_safe_area_vertical.setter
+    def action_safe_area_vertical(self, percentage):
+        if percentage == self.get_meta("pitivi::action_safe_area_vertical"):
+            return
+        self.set_meta("pitivi::action_safe_area_vertical", percentage)
+        self.emit("safe-area-size-changed")
+
+    @property
+    def action_safe_area_horizontal(self):
+        return self.get_meta("pitivi::action_safe_area_horizontal")
+
+    @action_safe_area_horizontal.setter
+    def action_safe_area_horizontal(self, percentage):
+        if percentage == self.get_meta("pitivi::action_safe_area_horizontal"):
+            return
+        self.set_meta("pitivi::action_safe_area_horizontal", percentage)
+        self.emit("safe-area-size-changed")
+
     # ------------------------------#
     # Proxy creation implementation #
     # ------------------------------#
@@ -2066,6 +2128,11 @@ class ProjectSettingsDialog:
         self.scaled_proxy_height_spin = self.builder.get_object("scaled_proxy_height")
         self.proxy_res_linked_check = self.builder.get_object("proxy_res_linked")
 
+        self.title_horizontal_spinbutton = self.builder.get_object("title_safe_area_horizontal")
+        self.title_vertical_spinbutton = self.builder.get_object("title_safe_area_vertical")
+        self.action_horizontal_spinbutton = self.builder.get_object("action_safe_area_horizontal")
+        self.action_vertical_spinbutton = self.builder.get_object("action_safe_area_vertical")
+
     def _setup_ui_constraints(self):
         """Creates the dynamic widgets and connects other widgets."""
         # Add custom framerate fraction widget.
@@ -2246,6 +2313,12 @@ class ProjectSettingsDialog:
         if matching_audio_preset:
             self.audio_presets_combo.set_active_id(matching_audio_preset)
 
+        # Safe Areas
+        self.title_vertical_spinbutton.set_value(self.project.title_safe_area_vertical * 100)
+        self.title_horizontal_spinbutton.set_value(self.project.title_safe_area_horizontal * 100)
+        self.action_vertical_spinbutton.set_value(self.project.action_safe_area_vertical * 100)
+        self.action_horizontal_spinbutton.set_value(self.project.action_safe_area_horizontal * 100)
+
         # Metadata
         self.author_entry.set_text(self.project.author)
         if self.project.year:
@@ -2268,6 +2341,12 @@ class ProjectSettingsDialog:
                 int(self.height_spinbutton.get_value()),
                 self.frame_rate_fraction_widget.get_widget_value())
 
+            # Store values as a decimal value
+            self.project.set_safe_areas_sizes(int(self.title_horizontal_spinbutton.get_value()) / 100,
+                                              int(self.title_vertical_spinbutton.get_value()) / 100,
+                                              int(self.action_horizontal_spinbutton.get_value()) / 100,
+                                              int(self.action_vertical_spinbutton.get_value()) / 100)
+
             self.project.audiochannels = get_combo_value(self.channels_combo)
             self.project.audiorate = get_combo_value(self.sample_rate_combo)
 
diff --git a/pitivi/viewer/overlay_stack.py b/pitivi/viewer/overlay_stack.py
index 5c67f376..c269107b 100644
--- a/pitivi/viewer/overlay_stack.py
+++ b/pitivi/viewer/overlay_stack.py
@@ -22,6 +22,7 @@ from gi.repository import Gtk
 
 from pitivi.utils.loggable import Loggable
 from pitivi.viewer.move_scale_overlay import MoveScaleOverlay
+from pitivi.viewer.safe_areas_overlay import SafeAreasOverlay
 from pitivi.viewer.title_overlay import TitleOverlay
 
 
@@ -65,6 +66,9 @@ class OverlayStack(Gtk.Overlay, Loggable):
         self.guidelines_overlay = guidelines_overlay
         self.add_overlay(guidelines_overlay)
 
+        self.safe_areas_overlay = SafeAreasOverlay(self)
+        self.add_overlay(self.safe_areas_overlay)
+
         sink_widget.connect("size-allocate", self.__sink_widget_size_allocate_cb)
 
     def __size_allocate_cb(self, widget, rectangle):
diff --git a/pitivi/viewer/safe_areas_overlay.py b/pitivi/viewer/safe_areas_overlay.py
new file mode 100644
index 00000000..339ee950
--- /dev/null
+++ b/pitivi/viewer/safe_areas_overlay.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+# Pitivi video editor
+# Copyright (c) 2020, Guy Richard <guy richard99 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, see <http://www.gnu.org/licenses/>.
+from gi.repository import Gtk
+
+from pitivi.utils.misc import round05
+
+
+class SafeAreasOverlay(Gtk.DrawingArea):
+    """Overlay showing the safe areas of a project."""
+
+    def __init__(self, stack):
+        Gtk.DrawingArea.__init__(self)
+
+        self.__project = stack.app.project_manager.current_project
+
+        self.__project.connect("safe-area-size-changed", self.__safe_areas_size_changed_cb)
+
+        self.props.no_show_all = True
+
+    def __safe_areas_size_changed_cb(self, project):
+        self.queue_draw()
+
+    @staticmethod
+    def _compute_rect(widget_width, widget_height, horizontal_factor, vertical_factor):
+        w = widget_width * horizontal_factor
+        h = widget_height * vertical_factor
+        x = (widget_width - w) / 2
+        y = (widget_height - h) / 2
+        return round05(x), round05(y), int(w), int(h)
+
+    def do_draw(self, cr):
+        width = self.get_allocated_width()
+        height = self.get_allocated_height()
+        title_rect = self._compute_rect(width, height, self.__project.title_safe_area_horizontal, 
self.__project.title_safe_area_vertical)
+        action_rect = self._compute_rect(width, height, self.__project.action_safe_area_horizontal, 
self.__project.action_safe_area_vertical)
+
+        # Black line borders.
+        cr.set_line_width(2)
+        cr.set_source_rgb(0, 0, 0)
+        cr.rectangle(*action_rect)
+        cr.rectangle(*title_rect)
+        cr.stroke()
+
+        # Lines inner color.
+        cr.set_line_width(1)
+
+        cr.set_source_rgb(1, 1, 0.75)
+        cr.rectangle(*action_rect)
+        cr.stroke()
+
+        cr.set_source_rgb(0.8, 1, 0.85)
+        cr.rectangle(*title_rect)
+        cr.stroke()
diff --git a/pitivi/viewer/viewer.py b/pitivi/viewer/viewer.py
index 783d2f72..621eaae4 100644
--- a/pitivi/viewer/viewer.py
+++ b/pitivi/viewer/viewer.py
@@ -347,9 +347,21 @@ class ViewerContainer(Gtk.Box, Loggable):
                                self.toggle_guidelines_action,
                                _("Toggle the currently selected composition guidelines"))
 
+        self.toggle_safe_areas_action = Gio.SimpleAction.new("toggle-safe-areas", None)
+        self.toggle_safe_areas_action.connect("activate", self.__toggle_safe_areas_cb)
+        self.action_group.add_action(self.toggle_safe_areas_action)
+        self.app.shortcuts.add("viewer.toggle-safe-areas",
+                               ["apostrophe"],
+                               self.toggle_safe_areas_action,
+                               _("Toggle safe areas on viewer"))
+
     def __toggle_guidelines_cb(self, unused_action, unused_parameter):
         self.guidelines_popover.toggle()
 
+    def __toggle_safe_areas_cb(self, unused_action, unused_parameter):
+        overlay = self.overlay_stack.safe_areas_overlay
+        overlay.set_visible(not overlay.get_visible())
+
     def __corner_draw_cb(self, unused_widget, cr, lines, space, margin):
         cr.set_line_width(1)
 
diff --git a/tests/test_project.py b/tests/test_project.py
index c3537b8a..592652b4 100644
--- a/tests/test_project.py
+++ b/tests/test_project.py
@@ -738,3 +738,21 @@ class TestExportSettings(common.TestCase):
         project.set_rendering(False)
         self.assertEqual(project.videowidth, 960)
         self.assertEqual(project.videoheight, 400)
+
+    def test_set_safe_area_sizes(self):
+        """Checks to ensure that the safe areas values are set correctly."""
+        project = common.create_project()
+        title_horizontal_factor = 0.8
+        title_vertical_factor = 0.9
+        action_horizontal_factor = 0.6
+        action_vertical_factor = 0.7
+
+        project.set_safe_areas_sizes(title_horizontal_factor,
+                                     title_vertical_factor,
+                                     action_horizontal_factor,
+                                     action_vertical_factor)
+
+        self.assertEqual(project.title_safe_area_horizontal, title_horizontal_factor)
+        self.assertEqual(project.title_safe_area_vertical, title_vertical_factor)
+        self.assertEqual(project.action_safe_area_horizontal, action_horizontal_factor)
+        self.assertEqual(project.action_safe_area_vertical, action_vertical_factor)
diff --git a/tests/test_viewer_safeareasoverlay.py b/tests/test_viewer_safeareasoverlay.py
new file mode 100644
index 00000000..014de530
--- /dev/null
+++ b/tests/test_viewer_safeareasoverlay.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Pitivi video editor
+# Copyright (c) 2020, Guy Richard <guy richard99 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, see <http://www.gnu.org/licenses/>.
+"""Tests for the pitivi.viewer.safe_areas_overlay module."""
+# pylint: disable=protected-access,no-self-use
+from pitivi.viewer.safe_areas_overlay import SafeAreasOverlay
+from tests import common
+
+
+class TestSafeAreasOverlay(common.TestCase):
+    """Tests for the SafeAreasOverlay class."""
+
+    def test_compute_rect(self):
+        """Checks the safe areas Cairo rect calculation."""
+        self.assertEqual(SafeAreasOverlay._compute_rect(200, 100, 1, 0.5), (0.5, 25.5, 200, 50))
+        self.assertEqual(SafeAreasOverlay._compute_rect(200, 100, 0.5, 1), (50.5, 0.5, 100, 100))
diff --git a/tests/test_viewer_viewer.py b/tests/test_viewer_viewer.py
new file mode 100644
index 00000000..4de424cb
--- /dev/null
+++ b/tests/test_viewer_viewer.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# Pitivi video editor
+# Copyright (c) 2020, Guy Richard <guy richard99 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, see <http://www.gnu.org/licenses/>.
+"""Tests for the pitivi.viewer.viewer module."""
+# pylint: disable=protected-access,no-self-use
+from pitivi.project import ProjectManager
+from pitivi.viewer.viewer import ViewerContainer
+from tests import common
+
+
+class TestViewerContainer(common.TestCase):
+
+    def test_toggle_safe_areas_action(self):
+        """Checks the effect of the toggle_safe_areas_action."""
+        app = common.create_pitivi_mock()
+        app.project_manager = ProjectManager(app)
+
+        viewer_container = ViewerContainer(app)
+
+        app.project_manager.new_blank_project()
+        self.assertFalse(viewer_container.overlay_stack.safe_areas_overlay.get_visible())
+
+        viewer_container.toggle_safe_areas_action.activate()
+        self.assertTrue(viewer_container.overlay_stack.safe_areas_overlay.get_visible())
+
+        viewer_container.toggle_safe_areas_action.activate()
+        self.assertFalse(viewer_container.overlay_stack.safe_areas_overlay.get_visible())


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