[gnome-clocks/bilelmoussaoui/redesign-timer] Redesign the timer panel



commit 3fdece623aa4946dd9cfa65b37778bba71a3ab9a
Author: Bilal Elmoussaoui <bil elmoussaoui gmail com>
Date:   Tue Jan 28 01:43:46 2020 +0100

    Redesign the timer panel

 data/css/gnome-clocks.css            |   5 +
 data/gnome-clocks.gresource.xml      |   2 +
 data/org.gnome.clocks.gschema.xml.in |  12 +-
 data/ui/timer.ui                     | 484 ++++----------------------------
 data/ui/timer_row.ui                 | 366 +++++++++++++++++++++++++
 data/ui/timer_setup.ui               | 355 ++++++++++++++++++++++++
 src/timer.vala                       | 517 ++++++++++++++++++++++++++---------
 7 files changed, 1162 insertions(+), 579 deletions(-)
---
diff --git a/data/css/gnome-clocks.css b/data/css/gnome-clocks.css
index 883bbed..ec51152 100644
--- a/data/css/gnome-clocks.css
+++ b/data/css/gnome-clocks.css
@@ -228,3 +228,8 @@ spinbutton.clocks-timer-label button {
     animation-timing-function: cubic-bezier(1.0,0,0,1.0);
     animation-duration: 1s; 
 }
+
+.round-button {
+  border-radius: 9999px;
+  padding: 18px 24px;
+}
diff --git a/data/gnome-clocks.gresource.xml b/data/gnome-clocks.gresource.xml
index 5a922b6..5c17cf5 100644
--- a/data/gnome-clocks.gresource.xml
+++ b/data/gnome-clocks.gresource.xml
@@ -19,6 +19,8 @@
     <file preprocess="xml-stripblanks">ui/stopwatch.ui</file>
     <file preprocess="xml-stripblanks">ui/stopwatchlapsrow.ui</file>
     <file preprocess="xml-stripblanks">ui/timer.ui</file>
+    <file preprocess="xml-stripblanks">ui/timer_row.ui</file>
+    <file preprocess="xml-stripblanks">ui/timer_setup.ui</file>
   </gresource>
   <gresource prefix="/org/gnome/clocks/icons">
     <file alias="globe-symbolic.svg">gtk/icons/globe-symbolic.svg</file>
diff --git a/data/org.gnome.clocks.gschema.xml.in b/data/org.gnome.clocks.gschema.xml.in
index 01f79c5..71128ff 100644
--- a/data/org.gnome.clocks.gschema.xml.in
+++ b/data/org.gnome.clocks.gschema.xml.in
@@ -14,12 +14,12 @@
         List of alarms set.
       </description>
     </key>
-    <key name="timer" type="u">
-      <default>300</default>
-      <summary>Timer</summary>
-      <description>
-        Configured timer duration in seconds.
-      </description>
+    <key name="timers" type="aa{sv}">
+       <default>[]</default>
+       <summary>Configured Timers</summary>
+       <description>
+       List of timers set.
+       </description>
     </key>
     <key name="geolocation" type="b">
       <default>true</default>
diff --git a/data/ui/timer.ui b/data/ui/timer.ui
index 7c37afe..ffaf337 100644
--- a/data/ui/timer.ui
+++ b/data/ui/timer.ui
@@ -1,472 +1,80 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
 <interface>
-  <!-- interface-requires gtk+ 3.6 -->
-  <object class="GtkAdjustment" id="adjustment_hours">
-    <property name="upper">99</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment_minutes">
-    <property name="upper">59</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment_seconds">
-    <property name="upper">59</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
+  <requires lib="gtk+" version="3.18"/>
+  <requires lib="libhandy" version="0.0"/>
   <template class="ClocksTimerFace" parent="GtkStack">
     <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <signal name="notify::visible-child" handler="visible_child_changed" swapped="no"/>
     <child>
-      <object class="ClocksAnalogFrame" id="setup_frame">
+      <object class="HdyColumn">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="margin_start">48</property>
-        <property name="margin_end">48</property>
-        <property name="margin_top">48</property>
-        <property name="margin_bottom">48</property>
+        <property name="maximum_width">600</property>
+        <property name="linear_growth_width">600</property>
         <child>
-          <object class="GtkGrid" id="setup_grid">
+          <object class="GtkListBox" id="timers_list">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <property name="halign">center</property>
-            <property name="valign">center</property>
-            <property name="hexpand">True</property>
-            <property name="vexpand">True</property>
-            <property name="row_spacing">24</property>
-            <child>
-              <object class="GtkGrid" id="grid_spinbuttons">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="halign">center</property>
-                <property name="valign">center</property>
-                <child>
-                  <object class="GtkLabel" id="dummy_label1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="label"></property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSpinButton" id="h_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="max_length">2</property>
-                    <property name="invisible_char">●</property>
-                    <property name="xalign">0.5</property>
-                    <property name="invisible_char_set">True</property>
-                    <property name="input_purpose">number</property>
-                    <property name="orientation">vertical</property>
-                    <property name="adjustment">adjustment_hours</property>
-                    <property name="numeric">True</property>
-                    <property name="width_chars">2</property>
-                    <property name="wrap">True</property>
-                    <signal name="output" handler="show_leading_zeros" swapped="no"/>
-                    <signal name="changed" handler="update_start_button" swapped="no"/>
-                    <style>
-                      <class name="clocks-spinbutton"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="colon_label1">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="label">∶</property>
-                    <style>
-                      <class name="clocks-timer-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSpinButton" id="m_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="max_length">2</property>
-                    <property name="invisible_char">●</property>
-                    <property name="xalign">0.5</property>
-                    <property name="invisible_char_set">True</property>
-                    <property name="input_purpose">number</property>
-                    <property name="orientation">vertical</property>
-                    <property name="adjustment">adjustment_minutes</property>
-                    <property name="numeric">True</property>
-                    <property name="width_chars">2</property>
-                    <property name="wrap">True</property>
-                    <signal name="output" handler="show_leading_zeros" swapped="no"/>
-                    <signal name="changed" handler="update_start_button" swapped="no"/>
-                    <signal name="input" handler="input_minutes" swapped="no"/>
-                    <style>
-                      <class name="clocks-spinbutton"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">3</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="colon_label2">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="label">∶</property>
-                    <style>
-                      <class name="clocks-timer-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">4</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSpinButton" id="s_spinbutton">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="max_length">2</property>
-                    <property name="invisible_char">●</property>
-                    <property name="xalign">0.5</property>
-                    <property name="invisible_char_set">True</property>
-                    <property name="input_purpose">number</property>
-                    <property name="orientation">vertical</property>
-                    <property name="adjustment">adjustment_seconds</property>
-                    <property name="numeric">True</property>
-                    <property name="width_chars">2</property>
-                    <property name="wrap">True</property>
-                    <signal name="output" handler="show_leading_zeros" swapped="no"/>
-                    <signal name="changed" handler="update_start_button" swapped="no"/>
-                    <signal name="input" handler="input_seconds" swapped="no"/>
-                    <style>
-                      <class name="clocks-spinbutton"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">5</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="dummy_label2">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="label"></property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">6</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">0</property>
-                <property name="width">1</property>
-                <property name="height">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkButton" id="start_button">
-                <property name="label" translatable="yes">Start</property>
-                <property name="width_request">280</property>
-                <property name="height_request">40</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <signal name="clicked" handler="on_start_button_clicked" swapped="no"/>
-                <style>
-                  <class name="clocks-button"/>
-                </style>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">1</property>
-                <property name="width">1</property>
-                <property name="height">1</property>
-              </packing>
-            </child>
+            <property name="margin_left">24</property>
+            <property name="margin_right">24</property>
+            <property name="margin_top">24</property>
+            <property name="margin_bottom">24</property>
+            <property name="selection_mode">none</property>
+            <property name="activate_on_single_click">False</property>
+            <style>
+              <class name="frame"/>
+            </style>
           </object>
         </child>
       </object>
+      <packing>
+        <property name="name">timers</property>
+      </packing>
     </child>
     <child>
-      <object class="ClocksTimerCountdownFrame" id="countdown_frame">
+      <object class="GtkBox" id="no_timer_container">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="margin_start">48</property>
-        <property name="margin_end">48</property>
-        <property name="margin_top">48</property>
-        <property name="margin_bottom">48</property>
+        <property name="halign">center</property>
+        <property name="valign">center</property>
+        <property name="orientation">vertical</property>
         <child>
-          <object class="GtkGrid" id="countdown_grid">
+          <placeholder/>
+        </child>
+        <child>
+          <object class="GtkButton" id="start_button">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
             <property name="halign">center</property>
             <property name="valign">center</property>
-            <property name="hexpand">True</property>
-            <property name="vexpand">True</property>
-            <property name="row_spacing">24</property>
             <child>
-              <object class="GtkGrid" id="grid_labels">
+              <object class="GtkImage">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="halign">center</property>
                 <property name="valign">center</property>
-                <child>
-                  <object class="GtkLabel" id="dummy_label3">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="label"></property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">0</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSpinButton" id="h_label">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="can_focus">True</property>
-                    <property name="max_length">2</property>
-                    <property name="invisible_char">●</property>
-                    <property name="xalign">0.5</property>
-                    <property name="invisible_char_set">True</property>
-                    <property name="input_purpose">number</property>
-                    <property name="orientation">vertical</property>
-                    <property name="adjustment">adjustment_hours</property>
-                    <property name="numeric">True</property>
-                    <property name="width_chars">2</property>
-                    <property name="wrap">True</property>
-                    <signal name="output" handler="show_leading_zeros" swapped="no"/>
-                    <signal name="changed" handler="update_start_button" swapped="no"/>
-                    <style>
-                      <class name="clocks-timer-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">1</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="colon_label3">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="label">∶</property>
-                    <style>
-                      <class name="clocks-timer-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">2</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSpinButton" id="m_label">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="can_focus">True</property>
-                    <property name="max_length">2</property>
-                    <property name="invisible_char">●</property>
-                    <property name="xalign">0.5</property>
-                    <property name="invisible_char_set">True</property>
-                    <property name="input_purpose">number</property>
-                    <property name="orientation">vertical</property>
-                    <property name="adjustment">adjustment_minutes</property>
-                    <property name="numeric">True</property>
-                    <property name="width_chars">2</property>
-                    <property name="wrap">True</property>
-                    <signal name="output" handler="show_leading_zeros" swapped="no"/>
-                    <signal name="changed" handler="update_start_button" swapped="no"/>
-                    <style>
-                      <class name="clocks-timer-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">3</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="colon_label4">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="label">∶</property>
-                    <style>
-                      <class name="clocks-timer-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">4</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkSpinButton" id="s_label">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="can_focus">True</property>
-                    <property name="max_length">2</property>
-                    <property name="invisible_char">●</property>
-                    <property name="xalign">0.5</property>
-                    <property name="invisible_char_set">True</property>
-                    <property name="input_purpose">number</property>
-                    <property name="orientation">vertical</property>
-                    <property name="adjustment">adjustment_seconds</property>
-                    <property name="numeric">True</property>
-                    <property name="width_chars">2</property>
-                    <property name="wrap">True</property>
-                    <signal name="output" handler="show_leading_zeros" swapped="no"/>
-                    <signal name="changed" handler="update_start_button" swapped="no"/>
-                    <style>
-                      <class name="clocks-timer-label"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="left_attach">5</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="dummy_label4">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="hexpand">True</property>
-                    <property name="label"></property>
-                  </object>
-                  <packing>
-                    <property name="left_attach">6</property>
-                    <property name="top_attach">0</property>
-                    <property name="width">1</property>
-                    <property name="height">1</property>
-                  </packing>
-                </child>
-              </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">0</property>
-                <property name="width">1</property>
-                <property name="height">1</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GtkBox" id="button_box">
-                <property name="visible">True</property>
-                <property name="can_focus">False</property>
-                <property name="hexpand">True</property>
-                <property name="homogeneous">True</property>
-                <property name="spacing">16</property>
-                <child>
-                  <object class="GtkButton" id="left_button">
-                    <property name="label" translatable="yes">Pause</property>
-                    <property name="width_request">132</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
-                    <signal name="clicked" handler="on_left_button_clicked" swapped="no"/>
-                    <style>
-                      <class name="clocks-button"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">0</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkButton" id="right_button">
-                    <property name="label" translatable="yes">Reset</property>
-                    <property name="width_request">132</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">True</property>
-                    <signal name="clicked" handler="on_right_button_clicked" swapped="no"/>
-                    <style>
-                      <class name="clocks-button"/>
-                    </style>
-                  </object>
-                  <packing>
-                    <property name="expand">True</property>
-                    <property name="fill">True</property>
-                    <property name="position">1</property>
-                  </packing>
-                </child>
+                <property name="icon_name">media-playback-start-symbolic</property>
               </object>
-              <packing>
-                <property name="left_attach">0</property>
-                <property name="top_attach">1</property>
-                <property name="width">1</property>
-                <property name="height">1</property>
-              </packing>
             </child>
+            <style>
+              <class name="round-button"/>
+              <class name="suggested-action"/>
+            </style>
           </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
         </child>
       </object>
+      <packing>
+        <property name="name">empty</property>
+        <property name="position">1</property>
+      </packing>
     </child>
   </template>
-  <object class="GtkSizeGroup" id="sizegroup1">
-    <widgets>
-      <widget name="grid_spinbuttons"/>
-      <widget name="start_button"/>
-      <widget name="grid_labels"/>
-      <widget name="button_box"/>
-    </widgets>
-  </object>
-  <object class="GtkSizeGroup" id="sizegroup2">
-    <property name="mode">vertical</property>
-    <widgets>
-      <widget name="grid_spinbuttons"/>
-      <widget name="grid_labels"/>
-    </widgets>
-  </object>
-  <object class="GtkSizeGroup" id="sizegroup3">
-    <property name="mode">vertical</property>
-    <widgets>
-      <widget name="start_button"/>
-      <widget name="button_box"/>
-    </widgets>
-  </object>
 </interface>
diff --git a/data/ui/timer_row.ui b/data/ui/timer_row.ui
new file mode 100644
index 0000000..519ffe3
--- /dev/null
+++ b/data/ui/timer_row.ui
@@ -0,0 +1,366 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <object class="GtkAdjustment" id="adjustment_hours">
+    <property name="upper">99</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment_minutes">
+    <property name="upper">59</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment_seconds">
+    <property name="upper">59</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <template class="ClocksTimerRow" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="valign">start</property>
+    <child>
+      <object class="GtkBox" id="timer_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkStack" id="timer_stack">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkBox" id="countdown_frame">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <property name="margin_left">48</property>
+                <property name="margin_right">48</property>
+                <property name="margin_start">48</property>
+                <property name="margin_end">48</property>
+                <property name="margin_top">48</property>
+                <property name="margin_bottom">48</property>
+                <child>
+                  <object class="GtkGrid" id="grid_labels">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <child>
+                      <object class="GtkLabel" id="dummy_label3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="h_label">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="max_length">2</property>
+                        <property name="invisible_char">●</property>
+                        <property name="width_chars">2</property>
+                        <property name="text" translatable="yes">0</property>
+                        <property name="xalign">0.5</property>
+                        <property name="input_purpose">number</property>
+                        <property name="orientation">vertical</property>
+                        <property name="adjustment">adjustment_hours</property>
+                        <property name="numeric">True</property>
+                        <property name="wrap">True</property>
+                        <signal name="changed" handler="update_start_button" swapped="no"/>
+                        <signal name="output" handler="show_leading_zeros" swapped="no"/>
+                        <style>
+                          <class name="clocks-timer-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="colon_label3">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label">∶</property>
+                        <style>
+                          <class name="clocks-timer-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">2</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="m_label">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="max_length">2</property>
+                        <property name="invisible_char">●</property>
+                        <property name="width_chars">2</property>
+                        <property name="text" translatable="yes">0</property>
+                        <property name="xalign">0.5</property>
+                        <property name="input_purpose">number</property>
+                        <property name="orientation">vertical</property>
+                        <property name="adjustment">adjustment_minutes</property>
+                        <property name="numeric">True</property>
+                        <property name="wrap">True</property>
+                        <signal name="changed" handler="update_start_button" swapped="no"/>
+                        <signal name="output" handler="show_leading_zeros" swapped="no"/>
+                        <style>
+                          <class name="clocks-timer-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">3</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="colon_label4">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label">∶</property>
+                        <style>
+                          <class name="clocks-timer-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">4</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="s_label">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="can_focus">True</property>
+                        <property name="max_length">2</property>
+                        <property name="invisible_char">●</property>
+                        <property name="width_chars">2</property>
+                        <property name="text" translatable="yes">0</property>
+                        <property name="xalign">0.5</property>
+                        <property name="input_purpose">number</property>
+                        <property name="orientation">vertical</property>
+                        <property name="adjustment">adjustment_seconds</property>
+                        <property name="numeric">True</property>
+                        <property name="wrap">True</property>
+                        <signal name="changed" handler="update_start_button" swapped="no"/>
+                        <signal name="output" handler="show_leading_zeros" swapped="no"/>
+                        <style>
+                          <class name="clocks-timer-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">5</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="dummy_label4">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">6</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="placeholder_text" translatable="yes">Title...</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkBox" id="actions_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkStack" id="start_stack">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="hhomogeneous">False</property>
+            <property name="vhomogeneous">False</property>
+            <child>
+              <object class="GtkButton" id="pause_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <signal name="clicked" handler="on_pause_button_clicked" swapped="no"/>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">media-playback-pause-symbolic</property>
+                  </object>
+                </child>
+                <style>
+                  <class name="flat"/>
+                </style>
+              </object>
+              <packing>
+                <property name="name">pause</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="start_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="halign">center</property>
+                <property name="valign">center</property>
+                <signal name="clicked" handler="on_start_button_clicked" swapped="no"/>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">media-playback-start-symbolic</property>
+                  </object>
+                </child>
+                <style>
+                  <class name="flat"/>
+                </style>
+              </object>
+              <packing>
+                <property name="name">start</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="reset_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <signal name="clicked" handler="on_reset_button_clicked" swapped="no"/>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="icon_name">view-refresh-symbolic</property>
+              </object>
+            </child>
+            <style>
+              <class name="flat"/>
+            </style>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="delete_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="icon_name">edit-delete-symbolic</property>
+              </object>
+            </child>
+            <style>
+              <class name="flat"/>
+            </style>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="pack_type">end</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkSeparator">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="pack_type">end</property>
+        <property name="position">2</property>
+      </packing>
+    </child>
+  </template>
+  <object class="GtkSizeGroup" id="sizegroup2">
+    <property name="mode">vertical</property>
+    <widgets>
+      <widget name="grid_spinbuttons"/>
+      <widget name="grid_labels"/>
+    </widgets>
+  </object>
+  <object class="GtkSizeGroup" id="sizegroup1"/>
+  <object class="GtkSizeGroup" id="sizegroup3">
+    <property name="mode">vertical</property>
+  </object>
+</interface>
+
diff --git a/data/ui/timer_setup.ui b/data/ui/timer_setup.ui
new file mode 100644
index 0000000..713ac59
--- /dev/null
+++ b/data/ui/timer_setup.ui
@@ -0,0 +1,355 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <requires lib="libhandy" version="0.0"/>
+  <object class="GtkAdjustment" id="adjustment_hours">
+    <property name="upper">99</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment_minutes">
+    <property name="upper">59</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="adjustment_seconds">
+    <property name="upper">59</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <template class="ClocksTimerSetup" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="HdyColumn">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="margin_top">36</property>
+        <property name="margin_bottom">36</property>
+        <property name="maximum_width">600</property>
+        <property name="linear_growth_width">600</property>
+        <property name="expand">True</property>
+        <property name="valign">center</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">24</property>
+            <child>
+              <object class="GtkGrid">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">start</property>
+                <property name="row_spacing">12</property>
+                <property name="column_spacing">12</property>
+                <child>
+                  <object class="GtkButton" id="predefined_1m">
+                    <property name="label" translatable="yes">1 m</property>
+                    <property name="width_request">70</property>
+                    <property name="height_request">40</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="predefined_2m">
+                    <property name="label" translatable="yes">2 m</property>
+                    <property name="width_request">70</property>
+                    <property name="height_request">40</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="predefined_3m">
+                    <property name="label" translatable="yes">3 m</property>
+                    <property name="width_request">70</property>
+                    <property name="height_request">40</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="predefined_5m">
+                    <property name="label" translatable="yes">5 m</property>
+                    <property name="width_request">70</property>
+                    <property name="height_request">40</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">3</property>
+                    <property name="top_attach">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="predefined_30m">
+                    <property name="label" translatable="yes">30 m</property>
+                    <property name="width_request">70</property>
+                    <property name="height_request">40</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="predefined_15m">
+                    <property name="label" translatable="yes">15 m</property>
+                    <property name="width_request">70</property>
+                    <property name="height_request">40</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="predefined_45m">
+                    <property name="label" translatable="yes">45 m</property>
+                    <property name="width_request">70</property>
+                    <property name="height_request">40</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">2</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="predefined_1h">
+                    <property name="label" translatable="yes">1 h</property>
+                    <property name="width_request">70</property>
+                    <property name="height_request">40</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">3</property>
+                    <property name="top_attach">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_left">6</property>
+                <property name="margin_right">6</property>
+                <property name="margin_start">12</property>
+                <property name="margin_end">12</property>
+                <property name="margin_top">12</property>
+                <property name="margin_bottom">12</property>
+                <child>
+                  <object class="GtkGrid">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="valign">center</property>
+                    <property name="column_spacing">6</property>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="h_spinbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="max_length">2</property>
+                        <property name="invisible_char">●</property>
+                        <property name="width_chars">2</property>
+                        <property name="text" translatable="yes">0</property>
+                        <property name="xalign">0.5</property>
+                        <property name="input_purpose">number</property>
+                        <property name="orientation">vertical</property>
+                        <property name="adjustment">adjustment_hours</property>
+                        <property name="numeric">True</property>
+                        <property name="wrap">True</property>
+                        <signal name="changed" handler="update_start_button" swapped="no"/>
+                        <signal name="output" handler="show_leading_zeros" swapped="no"/>
+                        <style>
+                          <class name="clocks-spinbutton"/>
+                          <class name="flat"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label">∶</property>
+                        <style>
+                          <class name="clocks-timer-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">2</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="m_spinbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="max_length">2</property>
+                        <property name="invisible_char">●</property>
+                        <property name="width_chars">2</property>
+                        <property name="text" translatable="yes">0</property>
+                        <property name="xalign">0.5</property>
+                        <property name="input_purpose">number</property>
+                        <property name="orientation">vertical</property>
+                        <property name="adjustment">adjustment_minutes</property>
+                        <property name="numeric">True</property>
+                        <property name="wrap">True</property>
+                        <signal name="changed" handler="update_start_button" swapped="no"/>
+                        <signal name="input" handler="input_minutes" swapped="no"/>
+                        <signal name="output" handler="show_leading_zeros" swapped="no"/>
+                        <style>
+                          <class name="clocks-spinbutton"/>
+                          <class name="flat"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">3</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label">∶</property>
+                        <style>
+                          <class name="clocks-timer-label"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">4</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSpinButton" id="s_spinbutton">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="max_length">2</property>
+                        <property name="invisible_char">●</property>
+                        <property name="width_chars">2</property>
+                        <property name="text" translatable="yes">0</property>
+                        <property name="xalign">0.5</property>
+                        <property name="input_purpose">number</property>
+                        <property name="orientation">vertical</property>
+                        <property name="adjustment">adjustment_seconds</property>
+                        <property name="numeric">True</property>
+                        <property name="wrap">True</property>
+                        <signal name="changed" handler="update_start_button" swapped="no"/>
+                        <signal name="input" handler="input_seconds" swapped="no"/>
+                        <signal name="output" handler="show_leading_zeros" swapped="no"/>
+                        <style>
+                          <class name="clocks-spinbutton"/>
+                          <class name="flat"/>
+                        </style>
+                      </object>
+                      <packing>
+                        <property name="left_attach">5</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">6</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/src/timer.vala b/src/timer.vala
index 3e437e1..b2de0fc 100644
--- a/src/timer.vala
+++ b/src/timer.vala
@@ -19,136 +19,207 @@
 namespace Clocks {
 namespace Timer {
 
-public class CountdownFrame : AnalogFrame {
-    public double span { get; set; default = 0; }
+public class Item : Object {
 
-    private double elapsed;
-    private double elapsed_before_pause;
 
-    private double get_progress () {
-        return span != 0 ? (elapsed_before_pause + elapsed) / span : 0;
+    public string name {
+        get {
+            return _name;
+        }
+        set {
+            // ignored
+        }
+    }
+
+    public int hours {
+        get {
+            return timer.hour;
+        }
+    }
+    public int minutes {
+        get {
+            return timer.minute;
+        }
+    }
+
+    public int seconds {
+        get {
+            return timer.second;
+        }
+    }
+
+    public int in_seconds() {
+        return timer.second + timer.minute * 60 + timer.hour * 3600;
+    }
+
+    private string _name;
+    public GLib.Time timer { get; set; }
+
+    public void serialize (GLib.VariantBuilder builder) {
+        builder.open (new GLib.VariantType ("a{sv}"));
+        builder.add ("{sv}", "time", new GLib.Variant.int32(in_seconds()));
+        if (name != null) {
+            builder.add ("{sv}", "name", new GLib.Variant.string(name));
+        }
+        builder.close ();
     }
 
-    public void update (double e) {
-        elapsed = e;
-        queue_draw ();
+    public static Item? deserialize (GLib.Variant time_variant) {
+        GLib.Time? time = null;
+        string? name = null;
+
+        foreach (var v in time_variant) {
+            var key = v.get_child_value (0).get_string ();
+            switch (key) {
+                case "time":
+                    time = GLib.Time.gm(v.get_child_value (1).get_child_value (0).get_int32());
+                    break;
+                case "name":
+                    name = v.get_child_value (1).get_child_value (0).get_string();
+                    break;
+            }
+        }
+        return time != null ? new Item (time, name) : null;
+    }
+
+    public Item (GLib.Time timer, string name) {
+        Object (name: name);
+        this.timer = timer;
+    }
+}
+
+
+public class TimersStore: Object, GLib.ListModel {
+    private ListStore store;
+
+    public TimersStore () {
+        store = new ListStore (typeof (Item));
+    }
+
+    public void add(Item timer) {
+       store.append (timer);
     }
 
-    public void pause () {
-        elapsed_before_pause += elapsed;
-        elapsed = 0;
+    public Type get_item_type () {
+        return store.get_item_type ();
     }
 
-    public void reset () {
-        elapsed_before_pause = 0;
-        elapsed = 0;
+    public uint get_n_items () {
+        return store.get_n_items ();
     }
 
-    public override void draw_progress (Cairo.Context cr, int center_x, int center_y, int radius) {
-        var progress = get_progress ();
-        var context = get_style_context ();
+    public Object? get_item (uint position) {
+        return store.get_item (position);
+    }
 
-        context.save ();
-        context.add_class ("progress");
 
-        var color = context.get_color (context.get_state ());
+    public Variant serialize () {
+        var builder = new GLib.VariantBuilder (new VariantType ("aa{sv}"));
+        var n = store.get_n_items ();
+        for (int i = 0; i < n; i++) {
+            var item = store.get_object (i) as Item;
+            item.serialize (builder);
+        }
+        return builder.end ();
+    }
 
-        cr.arc (center_x, center_y, radius - LINE_WIDTH / 2, 1.5  * Math.PI, (1.5 + (1 - progress) * 2 ) * 
Math.PI);
-        Gdk.cairo_set_source_rgba (cr, color);
-        cr.set_line_width (LINE_WIDTH);
-        cr.set_line_cap  (Cairo.LineCap.ROUND);
-        cr.stroke ();
+    public delegate Item? DeserializeItemFunc (Variant v);
 
-        context.restore ();
+    public void deserialize (Variant variant, DeserializeItemFunc deserialize_item) {
+        foreach (var v in variant) {
+            Item? i = deserialize_item (v);
+            if (i != null) {
+                store.append (i);
+            }
+        }
     }
 }
 
-[GtkTemplate (ui = "/org/gnome/clocks/ui/timer.ui")]
-public class Face : Gtk.Stack, Clocks.Clock {
-    public enum State {
-        STOPPED,
-        RUNNING,
-        PAUSED
+public class NewTimerDialog: Hdy.Dialog {
+
+    private new Gtk.Button add_button;
+    private Gtk.Button cancel_button;
+    private Setup timer_setup;
+
+    public NewTimerDialog (Gtk.Window parent, Item? timer) {
+        Object (transient_for: parent, title: timer != null ? _("Edit Timer") : _("New Timer"), 
use_header_bar: 1);
+        this.set_default_size (640, 360);
+
+        var headerbar = (Gtk.HeaderBar)this.get_header_bar ();
+        headerbar.set_show_close_button (false);
+
+        add_button = new Gtk.Button.with_label (_("Add"));
+        add_button.get_style_context ().add_class ("suggested-action");
+        add_button.set_sensitive (false);
+        add_button.show ();
+        headerbar.pack_end (add_button);
+
+        cancel_button = new Gtk.Button.with_label (_("Cancel"));
+        cancel_button.clicked.connect ( () => this.destroy ());
+        cancel_button.show ();
+        headerbar.pack_start (cancel_button);
+
+        timer_setup = new Setup ();
+        this.get_content_area ().add (timer_setup);
+
+
     }
+}
 
-    public PanelId panel_id { get; construct set; }
-    public ButtonMode button_mode { get; set; default = NONE; }
-    public ViewMode view_mode { get; set; default = NORMAL; }
-    public bool can_select { get; set; default = false; }
-    public bool n_selected { get; set; }
-    public string title { get; set; default = _("Clocks"); }
-    public string subtitle { get; set; }
-    // Translators: Tooltip for the + button
-    public string new_label { get; default = _("New Timer"); }
 
-    public State state { get; private set; default = State.STOPPED; }
+[GtkTemplate (ui = "/org/gnome/clocks/ui/timer_setup.ui")]
+public class Setup : Gtk.Box {
 
-    private GLib.Settings settings;
-    private double span;
-    private GLib.Timer timer;
-    private uint timeout_id;
-    private Utils.Bell bell;
-    private GLib.Notification notification;
     [GtkChild]
-    private AnalogFrame setup_frame;
+    private Gtk.Button predefined_1m;
     [GtkChild]
-    private Gtk.Grid grid_spinbuttons;
+    private Gtk.Button predefined_2m;
     [GtkChild]
-    private Gtk.Grid grid_labels;
+    private Gtk.Button predefined_3m;
     [GtkChild]
-    private Gtk.SpinButton h_spinbutton;
+    private Gtk.Button predefined_5m;
     [GtkChild]
-    private Gtk.SpinButton m_spinbutton;
-    [GtkChild]
-    private Gtk.SpinButton s_spinbutton;
+    private Gtk.Button predefined_15m;
     [GtkChild]
-    private Gtk.Button start_button;
+    private Gtk.Button predefined_30m;
     [GtkChild]
-    private CountdownFrame countdown_frame;
+    private Gtk.Button predefined_45m;
     [GtkChild]
-    // We cheat and use spibuttons also when displaying the time
-    // making them insensitive and hiding the +/- via css
-    // this is needed to ensure the text does not move in the transition
-    private Gtk.SpinButton h_label;
+    private Gtk.Button predefined_1h;
     [GtkChild]
-    private Gtk.SpinButton m_label;
+    private Gtk.SpinButton h_spinbutton;
     [GtkChild]
-    private Gtk.SpinButton s_label;
+    private Gtk.SpinButton m_spinbutton;
     [GtkChild]
-    private Gtk.Button left_button;
-
-    construct {
-        panel_id = TIMER;
-        transition_type = CROSSFADE;
-
-        settings = new GLib.Settings ("org.gnome.clocks");
-
-        span = 0;
-        timer = new GLib.Timer ();
+    private Gtk.SpinButton s_spinbutton;
 
-        timeout_id = 0;
-        destroy.connect(() => {
-            if (timeout_id != 0) {
-                GLib.Source.remove(timeout_id);
-                timeout_id = 0;
-            }
-        });
+    public Setup() {
+        predefined_1m.clicked.connect(() => this.update_timer(0, 1, 0));
+        predefined_2m.clicked.connect(() => this.update_timer(0, 2, 0));
+        predefined_3m.clicked.connect(() => this.update_timer(0, 3, 0));
+        predefined_5m.clicked.connect(() => this.update_timer(0, 5, 0));
+        predefined_15m.clicked.connect(() => this.update_timer(0, 15, 0));
+        predefined_30m.clicked.connect(() => this.update_timer(0, 30, 0));
+        predefined_45m.clicked.connect(() => this.update_timer(0, 45, 0));
+        predefined_1h.clicked.connect(() => this.update_timer(1, 0, 0));
 
-        bell = new Utils.Bell ("complete");
-        notification = new GLib.Notification (_("Time is up!"));
-        notification.set_body (_("Timer countdown finished"));
+    }
 
-        // Force LTR since we do not want to reverse [hh] : [mm] : [ss]
-        grid_spinbuttons.set_direction (Gtk.TextDirection.LTR);
-        grid_labels.set_direction (Gtk.TextDirection.LTR);
+    public Item get_timer() {
+        var h = this.h_spinbutton.get_value();
+        var m = this.m_spinbutton.get_value();
+        var s = this.s_spinbutton.get_value();
+        time_t total_seconds = (time_t)(h * 3600 + m * 60 + s);
 
-        reset ();
+        var time = GLib.Time.gm(total_seconds);
+        return (new Item (time, ""));
     }
 
-    public virtual signal void ring () {
-        var app = GLib.Application.get_default () as Clocks.Application;
-        app.send_notification ("timer-is-up", notification);
-        bell.ring_once ();
+
+    private void update_timer(int h, int m, int s) {
+        this.h_spinbutton.set_value(h);
+        this.m_spinbutton.set_value(m);
+        this.s_spinbutton.set_value(s);
     }
 
     [GtkCallback]
@@ -156,6 +227,18 @@ public class Face : Gtk.Stack, Clocks.Clock {
         spin_button.set_text ("%02i".printf(spin_button.get_value_as_int ()));
         return true;
     }
+    [GtkCallback]
+    private void update_start_button () {
+       /* var h = h_spinbutton.get_value_as_int ();
+        var m = m_spinbutton.get_value_as_int ();
+        var s = s_spinbutton.get_value_as_int ();
+
+        if (h != 0 || m != 0 || s != 0) {
+            start_button.set_sensitive (true);
+        } else {
+            start_button.set_sensitive (false);
+        }*/
+    }
 
     [GtkCallback]
     private int input_minutes (Gtk.SpinButton spin_button, out double new_value) {
@@ -164,13 +247,14 @@ public class Face : Gtk.Stack, Clocks.Clock {
         // if input entered is not within bounds then it will carry the
         // extra portion to hours field
         if (entered_value > 59) {
-            int current_hours = h_spinbutton.get_value_as_int ();
-            h_spinbutton.set_value (double.min (99, current_hours + entered_value / 60));
+           int current_hours = h_spinbutton.get_value_as_int ();
+           h_spinbutton.set_value (double.min (99, current_hours + entered_value / 60));
         }
         new_value = entered_value % 60;
         return 1;
     }
 
+
     [GtkCallback]
     private int input_seconds (Gtk.SpinButton spin_button, out double new_value) {
         int entered_value = int.parse (spin_button.get_text ());
@@ -186,43 +270,79 @@ public class Face : Gtk.Stack, Clocks.Clock {
                 new_minutes = new_minutes % 60;
             }
             m_spinbutton.set_value (new_minutes);
+
         }
         new_value = entered_value % 60;
         return 1;
     }
 
-    [GtkCallback]
-    private void update_start_button () {
-        var h = h_spinbutton.get_value_as_int ();
-        var m = m_spinbutton.get_value_as_int ();
-        var s = s_spinbutton.get_value_as_int ();
 
-        if (h != 0 || m != 0 || s != 0) {
-            start_button.set_sensitive (true);
-            start_button.get_style_context ().add_class ("suggested-action");
-        } else {
-            start_button.set_sensitive (false);
-            start_button.get_style_context ().remove_class ("suggested-action");
-        }
+}
+
+
+[GtkTemplate (ui = "/org/gnome/clocks/ui/timer_row.ui")]
+public class Row : Gtk.Box {
+    public enum State {
+        STOPPED,
+        RUNNING,
+        PAUSED
+    }
+
+    public State state { get; private set; default = State.STOPPED; }
+    public Item item { get; construct set; }
+
+    private double span;
+    private GLib.Timer timer;
+    private uint timeout_id;
+    [GtkChild]
+    private Gtk.Button start_button;
+    [GtkChild]
+    private Gtk.Box countdown_frame;
+    [GtkChild]
+    private Gtk.Stack start_stack;
+    [GtkChild]
+    // We cheat and use spibuttons also when displaying the time
+    // making them insensitive and hiding the +/- via css
+    // this is needed to ensure the text does not move in the transition
+    private Gtk.SpinButton h_label;
+    [GtkChild]
+    private Gtk.SpinButton m_label;
+    [GtkChild]
+    private Gtk.SpinButton s_label;
+
+    public Row (Item item) {
+        Object(item: item);
+        span = 0;
+        timer = new GLib.Timer ();
+
+        timeout_id = 0;
+        destroy.connect(() => {
+            if (timeout_id != 0) {
+                GLib.Source.remove(timeout_id);
+                timeout_id = 0;
+            }
+        });
+        reset ();
     }
 
+
     [GtkCallback]
     private void on_start_button_clicked () {
-        start ();
+        switch (state) {
+        case State.PAUSED:
+        case State.STOPPED:
+            start ();
+            break;
+        default:
+            assert_not_reached ();
+        }
     }
 
     [GtkCallback]
-    private void on_left_button_clicked () {
+    private void on_pause_button_clicked () {
         switch (state) {
         case State.RUNNING:
             pause ();
-            left_button.set_label (_("Continue"));
-            left_button.get_style_context ().add_class ("suggested-action");
-            break;
-        case State.PAUSED:
-            start ();
-            left_button.set_label (_("Pause"));
-            left_button.get_style_context ().remove_class("suggested-action");
             break;
         default:
             assert_not_reached ();
@@ -230,40 +350,40 @@ public class Face : Gtk.Stack, Clocks.Clock {
     }
 
     [GtkCallback]
-    private void on_right_button_clicked () {
+    private void on_reset_button_clicked () {
         reset ();
-        left_button.set_label (_("Pause"));
     }
 
     private void reset () {
         state = State.STOPPED;
         timer.reset ();
-        span = settings.get_uint ("timer");
-        h_spinbutton.value = (int) span / 3600;
-        m_spinbutton.value = (int) span % 3600 / 60;
-        s_spinbutton.value = span % 60;
-        left_button.get_style_context ().remove_class("clocks-go");
+        /*h_spinbutton.value = item.hours;
+        m_spinbutton.value = item.minutes;
+        s_spinbutton.value = item.seconds;
+        */
         countdown_frame.get_style_context ().remove_class ("clocks-paused");
-        start_button.set_sensitive (span > 0);
-        countdown_frame.reset ();
-        visible_child = setup_frame;
+        start_button.set_sensitive (item.in_seconds() > 0);
+        // timer_stack.visible_child = setup_frame;
+        start_stack.visible_child_name = "start";
     }
 
     private void start () {
         countdown_frame.get_style_context ().remove_class ("clocks-paused");
 
         if (state == State.STOPPED) {
-            var h = h_spinbutton.get_value_as_int ();
+           /* var h = h_spinbutton.get_value_as_int ();
             var m = m_spinbutton.get_value_as_int ();
             var s = s_spinbutton.get_value_as_int ();
 
             span = h * 3600 + m * 60 + s;
-            settings.set_uint ("timer", (uint) span);
-            countdown_frame.span = span;
-            visible_child = countdown_frame;
+            // settings.set_uint ("timer", (uint) span);
+            // countdown_frame.span = span;
+            timer_stack.visible_child = countdown_frame;
 
             update_countdown_label (h, m, s);
+            */
         }
+        start_stack.visible_child_name = "pause";
 
         state = State.RUNNING;
         timer.start ();
@@ -275,7 +395,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
             var e = timer.elapsed ();
             if (e >= span) {
                 reset ();
-                ring ();
+                // ring ();
                 timeout_id = 0;
                 return false;
             }
@@ -288,8 +408,7 @@ public class Face : Gtk.Stack, Clocks.Clock {
         state = State.PAUSED;
         timer.stop ();
         span -= timer.elapsed ();
-        countdown_frame.get_style_context ().add_class ("clocks-paused");
-        countdown_frame.pause ();
+        start_stack.visible_child_name = "start";
     }
 
     private void update_countdown (double elapsed) {
@@ -304,7 +423,6 @@ public class Face : Gtk.Stack, Clocks.Clock {
             double r;
             Utils.time_to_hms (t, out h, out m, out s, out r);
             update_countdown_label (h, m, s);
-            countdown_frame.update (elapsed);
         }
     }
 
@@ -315,9 +433,9 @@ public class Face : Gtk.Stack, Clocks.Clock {
     }
 
     public override void grab_focus () {
-        if (visible_child == setup_frame) {
+        /*if (timer_stack.visible_child == setup_frame) {
             start_button.grab_focus ();
-        }
+        }*/
     }
 
     public bool escape_pressed () {
@@ -331,5 +449,134 @@ public class Face : Gtk.Stack, Clocks.Clock {
     }
 }
 
+[GtkTemplate (ui = "/org/gnome/clocks/ui/timer.ui")]
+public class Face : Gtk.Stack, Clocks.Clock {
+    public enum State {
+        EMPTY,
+        RUNNING,
+    }
+
+    private Setup timer_setup;
+    [GtkChild]
+    private Gtk.ListBox timers_list;
+    [GtkChild]
+    private Gtk.Box no_timer_container;
+    [GtkChild]
+    private Gtk.Button start_button;
+
+    public PanelId panel_id { get; construct set; }
+    public ButtonMode button_mode { get; set; default = NONE; }
+    public ViewMode view_mode { get; set; default = NORMAL; }
+    public State state { get; set; default = State.EMPTY; }
+    public bool can_select { get; set; default = false; }
+    public bool n_selected { get; set; }
+    public string title { get; set; default = _("Clocks"); }
+    public string subtitle { get; set; }
+    // Translators: Tooltip for the + button
+    public string new_label { get; default = _("New Timer"); }
+
+    private TimersStore timers;
+    private GLib.Settings settings;
+    private Utils.Bell bell;
+    private GLib.Notification notification;
+
+    construct {
+        panel_id = TIMER;
+        transition_type = CROSSFADE;
+        timer_setup = new Setup ();
+
+        settings = new GLib.Settings ("org.gnome.clocks");
+        timers = new TimersStore();
+
+
+        timers_list.set_header_func ((Gtk.ListBoxUpdateHeaderFunc) Hdy.list_box_separator_header);
+        timers_list.bind_model (timers, (timer) => {
+            var timer_row = new Row ((Item)timer);
+            return timer_row;
+        });
+
+        timers.items_changed.connect(() => {
+            if (this.timers.get_n_items () > 0) {
+                this.set_visible_child_name ("timers");
+                this.button_mode = NEW;
+            } else {
+                this.set_visible_child_name ("empty");
+                this.button_mode = NONE;
+            }
+        });
+
+        bell = new Utils.Bell ("complete");
+        notification = new GLib.Notification (_("Time is up!"));
+        notification.set_body (_("Timer countdown finished"));
+
+        no_timer_container.add(timer_setup);
+        no_timer_container.reorder_child(timer_setup, 0);
+        set_visible_child_name ("empty");
+
+        // start_button.set_sensitive(false);
+        start_button.clicked.connect(() => {
+            var timer = this.timer_setup.get_timer();
+            this.add_timer(timer);
+        });
+        load ();
+    }
+
+
+    public void activate_new () {
+        var dialog = new NewTimerDialog ((Gtk.Window) get_toplevel (), null);
+        dialog.response.connect ((dialog, response) => {
+            if (response == 1) {
+                /* var alarm = new Item ();
+                ((SetupDialog) dialog).apply_to_alarm (alarm);
+                alarms.add (alarm);
+                save ();*/
+            }
+            dialog.destroy ();
+        });
+        dialog.show ();
+    }
+
+
+    private void add_timer (Item timer) {
+        timers.add (timer);
+        visible_child_name = "timers";
+        save ();
+    }
+
+    private void load () {
+        timers.deserialize (settings.get_value ("timers"), Item.deserialize);
+    }
+
+    private void save () {
+        settings.set_value ("timers", timers.serialize ());
+    }
+
+    public virtual signal void ring () {
+        var app = GLib.Application.get_default () as Clocks.Application;
+        app.send_notification ("timer-is-up", notification);
+        bell.ring_once ();
+    }
+
+
+    public override void grab_focus () {
+        /*if (visible_child == setup_frame) {
+            start_button.grab_focus ();
+        }
+        */
+    }
+
+    public bool escape_pressed () {
+        /*if (state == State.STOPPED) {
+            return false;
+        }
+
+        reset ();
+
+        return true;
+        */
+       return false;
+    }
+}
+
 } // namespace Timer
 } // namespace Clocks


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