[pitivi] clipproperties: Allow locking the aspect ratio



commit ca68b4c1e1995da4a807238edfe322fba632419f
Author: Orange4337 <soran2918 gmail com>
Date:   Fri Apr 23 10:19:53 2021 -0500

    clipproperties: Allow locking the aspect ratio
    
    Currently, the clip properties panel supports changing the size of clips
    using height and width spin buttons.
    
    In GIMP there is a function to lock this ratio while resizing, but in
    Pitivi users had to calculate it themselves.
    
    We added a button similar to the one in GIMP to the clip properties
    panel.
    
    The svg files were pulled directly from Gimp's files.
    https://gitlab.gnome.org/GNOME/gimp/-/blob/master/icons/Symbolic/scalable/gimp-vchain-broken-symbolic.svg
    https://gitlab.gnome.org/GNOME/gimp/-/blob/master/icons/Symbolic/scalable/gimp-vchain-symbolic.svg
    
    Fixes #2446

 data/pixmaps/chain-broken-symbolic.svg    | 217 ++++++++++++++++++++++++++++++
 data/pixmaps/chain-connected-symbolic.svg | 141 +++++++++++++++++++
 data/ui/cliptransformation.ui             |  27 +++-
 pitivi/clipproperties.py                  |  36 ++++-
 tests/test_clipproperties.py              |  48 +++++++
 5 files changed, 462 insertions(+), 7 deletions(-)
---
diff --git a/data/pixmaps/chain-broken-symbolic.svg b/data/pixmaps/chain-broken-symbolic.svg
new file mode 100644
index 000000000..adc01df01
--- /dev/null
+++ b/data/pixmaps/chain-broken-symbolic.svg
@@ -0,0 +1,217 @@
+<?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/licenses/by-sa/3.0/";
+   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";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   viewBox="0 0 23.999999 24.000042"
+   id="svg7384"
+   height="16"
+   width="16"
+   version="1.1"
+   inkscape:version="0.92.2 2405546, 2018-03-11"
+   sodipodi:docname="chain-broken-symbolic.svg">
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1366"
+     inkscape:window-height="741"
+     id="namedview1510"
+     showgrid="false"
+     inkscape:zoom="4.5858572"
+     inkscape:cx="-26.117858"
+     inkscape:cy="1.7796093"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg7384"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <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></dc:title>
+        <dc:contributor>
+          <cc:Agent>
+            <dc:title>Barbara Muraus, Jakub Steiner, Klaus Staedtler</dc:title>
+          </cc:Agent>
+        </dc:contributor>
+        <dc:description>Images originally created as the &quot;Art Libre&quot; icon set. Extended and 
adopted for GIMP and Pitivi</dc:description>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs7386">
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient8074">
+      <stop
+         id="stop8072"
+         offset="0"
+         style="stop-color:#be00be;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7561">
+      <stop
+         id="stop7558"
+         offset="0"
+         style="stop-color:#a5a5a5;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7548">
+      <stop
+         id="stop7546"
+         offset="0"
+         style="stop-color:#ebebeb;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7542">
+      <stop
+         id="stop7538"
+         offset="0"
+         style="stop-color:#c9c9c9;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0,-735328.32,170712.69,0,2464326300,577972450)"
+       osb:paint="solid"
+       id="linearGradient19282">
+      <stop
+         id="stop19284"
+         offset="0"
+         style="stop-color:#b4b4b4;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.34682586,0,0,0.30620888,-154.35209,-275.32372)"
+       osb:paint="solid"
+       id="linearGradient19282-4">
+      <stop
+         id="stop19284-0"
+         offset="0"
+         style="stop-color:#bebebe;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="translate(1002.5097,122.75542)"
+       gradientUnits="userSpaceOnUse"
+       y2="12"
+       x2="19"
+       y1="12"
+       x1="4"
+       id="linearGradient9944"
+       xlink:href="#linearGradient19282-4" />
+    <linearGradient
+       gradientTransform="translate(-668.33991,81.83697)"
+       gradientUnits="userSpaceOnUse"
+       y2="-74.000198"
+       x2="-626"
+       y1="-74.000198"
+       x1="-628"
+       id="linearGradient9950"
+       xlink:href="#linearGradient19282-4" />
+    <linearGradient
+       gradientTransform="translate(-668.33991,81.83697)"
+       gradientUnits="userSpaceOnUse"
+       y2="-64.000198"
+       x2="-626"
+       y1="-64.000198"
+       x1="-628"
+       id="linearGradient9956"
+       xlink:href="#linearGradient19282-4" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient19282-4"
+       id="linearGradient9956-9"
+       x1="-628"
+       y1="-64.000198"
+       x2="-626"
+       y2="-64.000198"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient19282-4"
+       id="linearGradient9950-2"
+       x1="-628"
+       y1="-74.000198"
+       x2="-626"
+       y2="-74.000198"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient19282-4"
+       id="linearGradient9944-7"
+       x1="4"
+       y1="12"
+       x2="19"
+       y2="12"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <g
+     style="display:inline"
+     transform="matrix(1.0008752,0,0,1,1.7398026e-5,-1028.3622)"
+     id="chain-broken"
+     inkscape:export-xdpi="96"
+     inkscape:export-ydpi="96"
+     inkscape:label="#chain-broken-16">
+    <rect
+       transform="rotate(-90)"
+       y="-1.7382812e-05"
+       x="-1052.3622"
+       height="23.979013"
+       width="24"
+       id="rect4380-4"
+       
style="opacity:0;fill:#bebebe;fill-opacity:1;stroke:none;stroke-width:0.99956268;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke
 fill markers" />
+    <g
+       inkscape:label="stock-hchain"
+       id="g6797-2"
+       style="display:inline"
+       transform="matrix(0,-1.4999997,1.4999997,0,-928.99981,1143.8625)">
+      <rect
+         ry="0"
+         rx="1"
+         transform="matrix(0,-1,-1,0,0,0)"
+         y="-67.000198"
+         x="-628"
+         height="6.0000029"
+         width="2"
+         id="rect6775-3"
+         style="fill:#2e3436;fill-opacity:1;stroke:none" />
+      <rect
+         ry="0"
+         rx="0.9375"
+         transform="matrix(0,-1,-1,0,0,0)"
+         y="-77.000198"
+         x="-628"
+         height="5.9999986"
+         width="2"
+         id="rect6777-4"
+         style="fill:#2e3436;fill-opacity:1;stroke:none" />
+      <path
+         inkscape:connector-curvature="0"
+         id="rect6791-5"
+         transform="matrix(0,0.6666668,-0.6666668,0,77.000215,619.33333)"
+         d="M 7,3 C 5.3380003,3 4,4.3380003 4,6 L 3.99649,8.9999947 H 6.9938647 L 7,7.5 C 
7.0033989,6.6690072 7.6690002,6 8.5,6 h 6 C 15.331,6 16,6.6690002 16,7.5 l -0.014,1.4999947 h 2.997377 L 19,6 
C 19.009212,4.3380259 17.662,3 16,3 Z M 3.996488,14.999995 4,18 c 0.00195,1.661999 1.3380003,3 3,3 h 9 c 
1.662,0 3,-1.338 3,-3 L 18.98337,14.999995 H 15.985993 L 16,16.5 c 0.0078,0.830964 -0.669,1.5 -1.5,1.5 h -6 C 
7.6690002,18 7,17.331 7,16.5 L 6.99386,14.999995 Z"
+         style="fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1.49999964"
+         sodipodi:nodetypes="ssccssssccssscssssccsssscc" />
+    </g>
+  </g>
+</svg>
\ No newline at end of file
diff --git a/data/pixmaps/chain-connected-symbolic.svg b/data/pixmaps/chain-connected-symbolic.svg
new file mode 100644
index 000000000..35afb0d8f
--- /dev/null
+++ b/data/pixmaps/chain-connected-symbolic.svg
@@ -0,0 +1,141 @@
+<?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/licenses/by-sa/3.0/";
+   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";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   viewBox="0 0 23.999999 24.000561"
+   id="svg7384"
+   height="16"
+   width="16"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="chain-connected">
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="640"
+     inkscape:window-height="480"
+     id="namedview1508"
+     showgrid="false"
+     inkscape:zoom="9.8333333"
+     inkscape:cx="-989.6638"
+     inkscape:cy="778.39861"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="0"
+     inkscape:current-layer="svg7384" />
+  <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>
+        <dc:contributor>
+          <cc:Agent>
+            <dc:title>Barbara Muraus, Jakub Steiner, Klaus Staedtler</dc:title>
+          </cc:Agent>
+        </dc:contributor>
+        <dc:description>Images originally created as the &quot;Art Libre&quot; icon set. Extended and 
adopted for GIMP and Pitivi</dc:description>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+    <title
+     id="title9167">Gnome Symbolic Icon Theme</title>
+  <defs
+     id="defs7386">
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient8074">
+      <stop
+         id="stop8072"
+         offset="0"
+         style="stop-color:#be00be;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7561">
+      <stop
+         id="stop7558"
+         offset="0"
+         style="stop-color:#a5a5a5;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7548">
+      <stop
+         id="stop7546"
+         offset="0"
+         style="stop-color:#ebebeb;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       osb:paint="solid"
+       id="linearGradient7542">
+      <stop
+         id="stop7538"
+         offset="0"
+         style="stop-color:#c9c9c9;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0,-735328.32,170712.69,0,2464326300,577972450)"
+       osb:paint="solid"
+       id="linearGradient19282">
+      <stop
+         id="stop19284"
+         offset="0"
+         style="stop-color:#b4b4b4;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.34682586,0,0,0.30620888,-154.35207,-275.32368)"
+       osb:paint="solid"
+       id="linearGradient19282-4">
+      <stop
+         id="stop19284-0"
+         offset="0"
+         style="stop-color:#bebebe;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="translate(1001.6641,152.94727)"
+       gradientUnits="userSpaceOnUse"
+       y2="164.94727"
+       x2="1017.1035"
+       y1="164.94727"
+       x1="1002.1035"
+       id="linearGradient9972"
+       xlink:href="#linearGradient19282-4" />
+  </defs>
+  <g
+     transform="translate(-543.85293,14.083971)"
+     style="display:inline"
+     id="stock">
+    <g
+       transform="matrix(0,-1,1,0,-484.50925,9.91659)"
+       id="chain-connected">
+      <g
+         transform="matrix(1.4999997,0,0,1.4999997,-91.500275,99.36239)"
+         style="display:inline"
+         id="g6797-1">
+        <path
+           id="rect6775-88"
+           transform="matrix(0,0.6666668,-0.6666668,0,178.96544,-46.068843)"
+           d="m 1009.5098,152.94727 c -0.7791,0 -1.4063,0.62718 -1.4063,1.40625 l 0,1.59375 -3,0 c -1.662,0 
-3,1.338 -3,3 l 0,12 c 0,1.662 1.338,3 3,3 l 3,0 0,1.5 c 0,0.831 0.669,1.5 1.5,1.5 0.831,0 1.5,-0.669 
1.5,-1.5 l 0,-1.5 3,0 c 1.662,0 3,-1.338 3,-3 l 0,-12 c 0,-1.662 -1.338,-3 -3,-3 l -3,0 0,-1.59375 c 
0,-0.77907 -0.6272,-1.40625 -1.4062,-1.40625 z m -2.9063,6 1.5,0 0,1.59375 c 0,0.77906 0.6272,1.40625 
1.4063,1.40625 l 0.1875,0 c 0.779,0 1.4062,-0.62719 1.4062,-1.40625 l 0,-1.59375 1.5,0 c 0.831,0 1.5,0.669 
1.5,1.5 l 0,9 c 0,0.831 -0.669,1.5 -1.5,1.5 l -1.5,0 0,-1.5 c 0,-0.831 -0.669,-1.5 -1.5,-1.5 -0.831,0 
-1.5,0.669 -1.5,1.5 l 0,1.5 -1.5,0 c -0.831,0 -1.5,-0.669 -1.5,-1.5 l 0,-9 c 0,-0.831 0.669,-1.5 1.5,-1.5 z"
+           
style="fill:url(#linearGradient9972);fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1.49999964"
+           inkscape:connector-curvature="0" />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/data/ui/cliptransformation.ui b/data/ui/cliptransformation.ui
index b4e4b839d..fd1171a66 100644
--- a/data/ui/cliptransformation.ui
+++ b/data/ui/cliptransformation.ui
@@ -9,6 +9,11 @@
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
   </object>
+  <object class="GtkImage" id="aspect_ratio_image">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="icon_name">chain-connected-symbolic</property>
+  </object>
   <object class="GtkImage" id="icon_reset1">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -42,7 +47,7 @@
     <property name="margin_bottom">6</property>
     <property name="row_spacing">6</property>
     <property name="column_spacing">6</property>
-    <property name="row_homogeneous">True</property>
+    <property name="row_homogeneous">False</property>
     <child>
       <object class="GtkLabel" id="label11">
         <property name="visible">True</property>
@@ -69,7 +74,6 @@
       <packing>
         <property name="left_attach">1</property>
         <property name="top_attach">0</property>
-        <property name="width">3</property>
       </packing>
     </child>
     <child>
@@ -85,7 +89,6 @@
       <packing>
         <property name="left_attach">1</property>
         <property name="top_attach">1</property>
-        <property name="width">3</property>
       </packing>
     </child>
     <child>
@@ -114,7 +117,6 @@
       <packing>
         <property name="left_attach">1</property>
         <property name="top_attach">2</property>
-        <property name="width">3</property>
       </packing>
     </child>
     <child>
@@ -129,7 +131,22 @@
       <packing>
         <property name="left_attach">1</property>
         <property name="top_attach">3</property>
-        <property name="width">3</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkButton" id="aspect_ratio_button">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="focus_on_click">True</property>
+        <property name="tooltip_text" translatable="yes">Constrain Proportions</property>
+        <property name="receives_default">True</property>
+        <property name="image">aspect_ratio_image</property>
+        <property name="relief">none</property>
+      </object>
+      <packing>
+        <property name="left_attach">2</property>
+        <property name="top_attach">2</property>
+        <property name="height">2</property>
       </packing>
     </child>
     <child>
diff --git a/pitivi/clipproperties.py b/pitivi/clipproperties.py
index 8f632ac28..59bb166c9 100644
--- a/pitivi/clipproperties.py
+++ b/pitivi/clipproperties.py
@@ -905,6 +905,7 @@ class TransformationProperties(Gtk.Expander, Loggable):
         self.spin_buttons_handler_ids = {}
         self.set_label(_("Transformation"))
         self.set_expanded(True)
+        self._aspect_ratio: Optional[Gst.Fraction] = None
 
         self.builder = Gtk.Builder()
         self.builder.add_from_file(os.path.join(get_ui_dir(),
@@ -950,6 +951,14 @@ class TransformationProperties(Gtk.Expander, Loggable):
             self.__set_prop("posx", x)
             self.__set_prop("posy", y)
 
+    def _update_aspect_ratio_button_image(self):
+        image = self.builder.get_object("aspect_ratio_image")
+        if self._aspect_ratio is not None:
+            icon_name = "chain-connected-symbolic"
+        else:
+            icon_name = "chain-broken-symbolic"
+        image.props.icon_name = icon_name
+
     def _init_buttons(self):
         clear_button = self.builder.get_object("clear_button")
         clear_button.connect("clicked", self._default_values_cb)
@@ -977,6 +986,10 @@ class TransformationProperties(Gtk.Expander, Loggable):
         self.__setup_spin_button("width_spinbtn", "width")
         self.__setup_spin_button("height_spinbtn", "height")
 
+        self.aspect_ratio_button = self.builder.get_object("aspect_ratio_button")
+        self.aspect_ratio_button.connect("clicked", self._aspect_ratio_button_clicked_cb)
+        self._update_aspect_ratio_button_image()
+
     def __get_keyframes_timestamps(self):
         keyframes_ts = []
         for prop in ["posx", "posy", "width", "height"]:
@@ -1196,12 +1209,22 @@ class TransformationProperties(Gtk.Expander, Loggable):
         self.spin_buttons[property_name] = spinbtn
         self.spin_buttons_handler_ids[property_name] = handler_id
 
+    def _aspect_ratio_button_clicked_cb(self, aspect_ratio_button):
+        if self._aspect_ratio is None:
+            res, width = self.__get_source_property("width")
+            assert res
+            res, height = self.__get_source_property("height")
+            assert res
+            self._aspect_ratio = Gst.Fraction(width, height)
+        else:
+            self._aspect_ratio = None
+        self._update_aspect_ratio_button_image()
+
     def _on_value_changed_cb(self, spinbtn, prop):
         if not self.source:
             return
 
-        value = spinbtn.get_value()
-
+        value = int(spinbtn.get_value())
         res, cvalue = self.__get_source_property(prop)
         if not res:
             return
@@ -1211,6 +1234,15 @@ class TransformationProperties(Gtk.Expander, Loggable):
                                              
finalizing_action=CommitTimelineFinalizingAction(self._project.pipeline),
                                              toplevel=True):
                 self.__set_prop(prop, value)
+                if self._aspect_ratio is not None:
+                    if prop == "width":
+                        fraction = value / self._aspect_ratio
+                        height = int(fraction.num / fraction.denom)
+                        self.__set_prop("height", height)
+                    if prop == "height":
+                        fraction = value * self._aspect_ratio
+                        width = int(fraction.num / fraction.denom)
+                        self.__set_prop("width", width)
             self.app.gui.editor.viewer.overlay_stack.update(self.source)
 
     def set_source(self, source):
diff --git a/tests/test_clipproperties.py b/tests/test_clipproperties.py
index bd95c4c0c..3608a046f 100644
--- a/tests/test_clipproperties.py
+++ b/tests/test_clipproperties.py
@@ -57,6 +57,54 @@ class TransformationPropertiesTest(common.TestCase):
             spin_btn_value = spin_buttons[prop].get_value_as_int()
             self.assertEqual(new_val, spin_btn_value)
 
+    @common.setup_timeline
+    @common.setup_clipproperties
+    def test_clip_size_aspect_ratio_lock(self):
+        """Checks if aspect ratio is maintained when clip size is linked."""
+        # Add a clip and select it
+        clip = self.add_clips_simple(self.timeline_container.timeline, 1)[0]
+        self.timeline_container.timeline.selection.select([clip])
+        source = self.transformation_box.source
+        self.assertIsNotNone(source)
+
+        self._check_aspect_ratio_constraining(source, initial_size=(960, 400), width=1440, height=None, 
expected_width=None, expected_height=600)
+        self._check_aspect_ratio_constraining(source, initial_size=(320, 240), width=None, height=720, 
expected_width=960, expected_height=None)
+        self._check_aspect_ratio_constraining(source, initial_size=(100, 100), width=25, height=None, 
expected_width=None, expected_height=25)
+
+    def _check_aspect_ratio_constraining(self, source, initial_size, width, height, expected_width, 
expected_height):
+        width_spin = self.transformation_box.spin_buttons["width"]
+        height_spin = self.transformation_box.spin_buttons["height"]
+        width_spin.set_value(initial_size[0])
+        height_spin.set_value(initial_size[1])
+
+        # Lock the aspect ratio.
+        self.clipproperties.transformation_expander._aspect_ratio_button_clicked_cb(None)
+        self.assertIsNotNone(self.clipproperties.transformation_expander._aspect_ratio)
+
+        # Make a change to one of the spin button's value.
+        if width is not None:
+            width_spin.set_value(width)
+            expected_width = width
+        if height is not None:
+            height_spin.set_value(height)
+            expected_height = height
+        self.assertEqual(source.get_child_property("width"), (True, expected_width))
+        self.assertEqual(source.get_child_property("height"), (True, expected_height))
+
+        # Unlock the aspect ratio.
+        self.clipproperties.transformation_expander._aspect_ratio_button_clicked_cb(None)
+        self.assertIsNone(self.clipproperties.transformation_expander._aspect_ratio)
+
+        # Change the width independently.
+        width_spin.set_value(expected_width * 2)
+        self.assertEqual(source.get_child_property("width"), (True, expected_width * 2))
+        self.assertEqual(source.get_child_property("height"), (True, expected_height))
+
+        # Change the height independently.
+        height_spin.set_value(expected_height * 4)
+        self.assertEqual(source.get_child_property("width"), (True, expected_width * 2))
+        self.assertEqual(source.get_child_property("height"), (True, expected_height * 4))
+
     @common.setup_timeline
     @common.setup_clipproperties
     def test_spin_buttons_write(self):


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