1/* 2 * Copyright (c) 2016 gnome-pomodoro contributors 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * Authors: Kamil Prusko <kamilprusko@gmail.com> 18 * 19 */ 20 21 22namespace Pomodoro 23{ 24 [GtkTemplate (ui = "/org/gnome/pomodoro/window.ui")] 25 public class Window : Gtk.ApplicationWindow, Gtk.Buildable 26 { 27 private const int MIN_WIDTH = 500; 28 private const int MIN_HEIGHT = 650; 29 30 private const double FADED_IN = 1.0; 31 private const double FADED_OUT = 0.2; 32 33 private const double TIMER_LINE_WIDTH = 6.0; 34 private const double TIMER_RADIUS = 165.0; 35 36 private struct Name 37 { 38 public string name; 39 public string display_name; 40 } 41 42 private const Name[] STATE_NAMES = { 43 { "null", "" }, 44 { "pomodoro", N_("Pomodoro") }, 45 { "short-break", N_("Short Break") }, 46 { "long-break", N_("Long Break") } 47 }; 48 49 public string mode { 50 get { 51 return this.stack.visible_child_name; 52 } 53 set { 54 this.stack.visible_child_name = value; 55 } 56 } 57 58 public string default_mode { 59 get { 60 return this.default_page; 61 } 62 } 63 64 private unowned Pomodoro.Timer timer; 65 66 [GtkChild] 67 private unowned Gtk.Stack stack; 68 [GtkChild] 69 private unowned Gtk.Stack timer_stack; 70 [GtkChild] 71 private unowned Gtk.ToggleButton state_togglebutton; 72 [GtkChild] 73 private unowned Gtk.Label minutes_label; 74 [GtkChild] 75 private unowned Gtk.Label seconds_label; 76 [GtkChild] 77 private unowned Gtk.Widget timer_box; 78 [GtkChild] 79 private unowned Gtk.Button pause_button; 80 [GtkChild] 81 private unowned Gtk.Image pause_button_image; 82 83 private Pomodoro.Animation blink_animation; 84 private string default_page; 85 86 construct 87 { 88 var geometry = Gdk.Geometry () { 89 min_width = MIN_WIDTH, 90 max_width = -1, 91 min_height = MIN_HEIGHT, 92 max_height = -1 93 }; 94 this.set_geometry_hints (this, geometry, Gdk.WindowHints.MIN_SIZE); 95 96 // this.stack.add_titled (this.timer_stack, "timer", _("Timer")); 97 this.stack.add_titled (new Pomodoro.StatsView (), "stats", _("Stats")); 98 99 // TODO: this.default_page should be set from application.vala 100 var application = Pomodoro.Application.get_default (); 101 102 // if (application.capabilities.has_enabled ("task-list")) { // TODO 103 // this.default_page = "task-list"; 104 // } 105 if (application.capabilities.has_capability ("indicator")) { 106 this.default_page = "stats"; 107 } 108 else { 109 this.default_page = "timer"; 110 } 111 112 this.stack.visible_child_name = this.default_page; 113 114 this.on_timer_state_notify (); 115 this.on_timer_elapsed_notify (); 116 this.on_timer_is_paused_notify (); 117 } 118 119 public void parser_finished (Gtk.Builder builder) 120 { 121 this.timer = Pomodoro.Timer.get_default (); 122 this.insert_action_group ("timer", this.timer.get_action_group ()); 123 124 base.parser_finished (builder); 125 126 var state_togglebutton = builder.get_object ("state_togglebutton"); 127 state_togglebutton.bind_property ("active", 128 builder.get_object ("state_popover"), 129 "visible", 130 GLib.BindingFlags.BIDIRECTIONAL); 131 132 this.timer.notify["state"].connect_after (this.on_timer_state_notify); 133 this.timer.notify["elapsed"].connect_after (this.on_timer_elapsed_notify); 134 this.timer.notify["is-paused"].connect_after (this.on_timer_is_paused_notify); 135 } 136 137 private void on_blink_animation_complete () 138 { 139 if (this.timer.is_paused) { 140 this.blink_animation.start_with_value (1.0); 141 } 142 } 143 144 private void on_timer_state_notify () 145 { 146 this.timer_stack.visible_child_name = 147 (this.timer.state is Pomodoro.DisabledState) ? "disabled" : "enabled"; 148 149 foreach (var mapping in STATE_NAMES) 150 { 151 if (mapping.name == this.timer.state.name && mapping.display_name != "") { 152 this.state_togglebutton.label = mapping.display_name; 153 break; 154 } 155 } 156 } 157 158 private void on_timer_elapsed_notify () 159 { 160 if (!(this.timer.state is Pomodoro.DisabledState)) 161 { 162 var remaining = (uint) double.max (Math.ceil (this.timer.remaining), 0.0); 163 var minutes = remaining / 60; 164 var seconds = remaining % 60; 165 166 this.minutes_label.label = "%02u".printf (minutes); 167 this.seconds_label.label = "%02u".printf (seconds); 168 169 this.timer_box.queue_draw (); 170 } 171 } 172 173 private void on_timer_is_paused_notify () 174 { 175 if (this.blink_animation != null) { 176 this.blink_animation.stop (); 177 this.blink_animation = null; 178 } 179 180 if (this.timer.is_paused) { 181 this.pause_button_image.icon_name = "media-playback-start-symbolic"; 182 this.pause_button.action_name = "timer.resume"; 183 this.pause_button.tooltip_text = _("Resume"); 184 185 this.blink_animation = new Pomodoro.Animation (Pomodoro.AnimationMode.BLINK, 186 2500, 187 5); 188 this.blink_animation.add_property (this.timer_box, 189 "opacity", 190 FADED_OUT); 191 this.blink_animation.complete.connect (this.on_blink_animation_complete); 192 this.blink_animation.start_with_value (1.0); 193 } 194 else { 195 this.pause_button_image.icon_name = "media-playback-pause-symbolic"; 196 this.pause_button.action_name = "timer.pause"; 197 this.pause_button.tooltip_text = _("Pause"); 198 199 this.blink_animation = new Pomodoro.Animation (Pomodoro.AnimationMode.EASE_OUT, 200 200, 201 50); 202 this.blink_animation.add_property (this.timer_box, 203 "opacity", 204 1.0); 205 this.blink_animation.start (); 206 } 207 } 208 209 [GtkCallback] 210 private bool on_timer_box_draw (Gtk.Widget widget, 211 Cairo.Context context) 212 { 213 if (!(this.timer.state is Pomodoro.DisabledState)) 214 { 215 var style_context = widget.get_style_context (); 216 var color = style_context.get_color (widget.get_state_flags ()); 217 218 var width = widget.get_allocated_width (); 219 var height = widget.get_allocated_height (); 220 var x = 0.5 * width; 221 var y = 0.5 * height; 222 var progress = this.timer.state_duration > 0.0 223 ? this.timer.elapsed / this.timer.state_duration : 0.0; 224 225 var angle1 = - 0.5 * Math.PI - 2.0 * Math.PI * progress.clamp (0.000001, 1.0); 226 var angle2 = - 0.5 * Math.PI; 227 228 context.set_line_width (TIMER_LINE_WIDTH); 229 230 context.set_source_rgba (color.red, 231 color.green, 232 color.blue, 233 color.alpha * 0.1); 234 context.arc (x, y, TIMER_RADIUS, 0.0, 2 * Math.PI); 235 context.stroke (); 236 237 context.set_line_cap (Cairo.LineCap.ROUND); 238 context.set_source_rgba (color.red, 239 color.green, 240 color.blue, 241 color.alpha * FADED_IN - (color.alpha * 0.1) * (1.0 - FADED_IN)); 242 243 context.arc_negative (x, y, TIMER_RADIUS, angle1, angle2); 244 context.stroke (); 245 } 246 247 return false; 248 } 249 250 [GtkCallback] 251 private bool on_button_press (Gtk.Widget widget, 252 Gdk.EventButton event) 253 { 254 if (event.button == 1) { 255 this.begin_move_drag ((int) event.button, (int) event.x_root, (int) event.y_root, event.time); 256 257 return true; 258 } 259 260 return false; 261 } 262 } 263} 264