11import math
2+ import time
23import gi
34gi .require_version ('Gtk' , '4.0' )
45gi .require_version ('Adw' , '1' )
@@ -23,38 +24,42 @@ def __init__(self, **kwargs):
2324 self .timer_running = False
2425 self .remaining_seconds = 0
2526 self .total_seconds = 0
27+ self .elapsed_time = 0
28+ self .start_time = 0
2629 self .timeout_id = None
2730
28- # Create start button
29- self .start_button = Gtk .Button (
30- icon_name = "media-playback-start-symbolic" ,
31- css_classes = ["circular" ]
32- )
33- self .start_button .set_valign (Gtk .Align .END )
31+ # Create buttons programmatically
32+ self .start_button = Gtk .Button ()
33+ self .start_button .set_icon_name ("media-playback-start-symbolic" )
34+ self .start_button .add_css_class ("circular" )
35+ self .start_button .add_css_class ("raised" )
3436 self .start_button .connect ('clicked' , self ._on_start_clicked )
35- self .button_box .append (self .start_button )
3637
37- # Create reset button
38- self .reset_button = Gtk . Button (
39- icon_name = "view-refresh-symbolic" ,
40- css_classes = [ "circular" , "destructive-action" ]
41- )
42- self .reset_button .set_valign ( Gtk . Align . END )
38+ self . reset_button = Gtk . Button ()
39+ self .reset_button . set_icon_name ( "edit-undo-symbolic" )
40+ self . reset_button . add_css_class ( "circular" )
41+ self . reset_button . add_css_class ( "small" )
42+ self . reset_button . add_css_class ( "destructive-action" )
43+ self .reset_button .set_visible ( False )
4344 self .reset_button .connect ('clicked' , self ._on_reset_clicked )
44- self .reset_button .set_visible (False ) # Initially hidden
45- self .button_box .append (self .reset_button )
4645
47- # Set up drawing area
48- self .progress_circle .set_draw_func (self .draw_timer_arc , None )
46+ # Add buttons to GtkFixed and set positions
47+ self .button_box .put (self .start_button , 15 , 0 )
48+ self .button_box .put (self .reset_button , 65 , 0 )
4949
5050 # Connect signals
5151 self .minutes_spin .connect ('value-changed' , self ._on_minutes_changed )
5252
53+ # Set up drawing area
54+ self .progress_circle .set_draw_func (self .draw_timer_arc , None )
55+
5356 # Add CSS for styling
5457 css_provider = Gtk .CssProvider ()
5558 css_provider .load_from_data (b"""
5659 .timer-label { font-size: 48px; font-weight: 300; }
60+ .button-box { min-width: 300px; }
5761 button.circular { padding: 4px; margin: 2px; border-radius: 9999px; }
62+ button.small { padding: 2px; }
5863 headerbar { padding: 0; min-height: 24px; box-shadow: none; border-bottom: none; }
5964 window { margin: 0; }
6065 """ )
@@ -71,9 +76,12 @@ def __init__(self, **kwargs):
7176 def _on_reset_clicked (self , button ):
7277 self .remaining_seconds = 0
7378 self .total_seconds = 0
79+ self .elapsed_time = 0
7480 self ._update_time_label ()
7581 self .reset_button .set_visible (False )
82+ self .minutes_spin .set_sensitive (True )
7683 self .progress_circle .queue_draw ()
84+ self ._update_start_button_state ()
7785
7886 def _on_start_clicked (self , button ):
7987 if not self .timer_running :
@@ -82,25 +90,31 @@ def _on_start_clicked(self, button):
8290 if minutes > 0 :
8391 self .total_seconds = minutes * 60
8492 self .remaining_seconds = self .total_seconds
93+ if self .remaining_seconds == 0 :
94+ self ._on_reset_clicked (None ) # Reset the timer if it has finished naturally
95+ minutes = self .minutes_spin .get_value_as_int ()
96+ if minutes > 0 :
97+ self .total_seconds = minutes * 60
98+ self .remaining_seconds = self .total_seconds
99+ self .start_time = time .time () - self .elapsed_time
85100 self ._start_timer ()
86101 button .set_icon_name ("media-playback-stop-symbolic" )
87102 else :
88103 self ._stop_timer ()
89104 button .set_icon_name ("media-playback-start-symbolic" )
90- if self .remaining_seconds > 0 :
91- self .reset_button .set_visible (True )
105+ self .reset_button .set_visible (True )
92106
93107 def draw_timer_arc (self , area , cr , width , height , data ):
94108 center_x = width / 2
95109 center_y = height / 2
96110 radius = min (width , height ) / 2 - 10
97- cr .set_source_rgba (0.9 , 0.9 , 0.9 , 0.2 )
111+ cr .set_source_rgba (0.7686 , 0.7686 , 0.7686 , 0.5 ) # Converted from #c4c4c4
98112 cr .set_line_width (8 )
99113 cr .arc (center_x , center_y , radius , 0 , 2 * math .pi )
100114 cr .stroke ()
101115 if self .total_seconds > 0 :
102116 cr .set_source_rgb (0.2 , 0.6 , 1.0 )
103- progress = 1 - ( self .remaining_seconds / self .total_seconds )
117+ progress = self .elapsed_time / self .total_seconds
104118 cr .arc (center_x , center_y , radius , - math .pi / 2 ,
105119 2 * math .pi * progress - math .pi / 2 )
106120 cr .stroke ()
@@ -119,38 +133,47 @@ def _update_start_button_state(self):
119133
120134 def _start_timer (self ):
121135 self .timer_running = True
122- self .timeout_id = GLib .timeout_add_seconds ( 1 , self ._update_timer )
136+ self .timeout_id = GLib .timeout_add ( 16 , self ._update_timer ) # Update every ~16ms for 60fps
123137 self .minutes_spin .set_sensitive (False )
138+ self .reset_button .set_visible (False )
124139
125140 def _stop_timer (self ):
126141 self .timer_running = False
127142 if self .timeout_id :
128143 GLib .source_remove (self .timeout_id )
129144 self .timeout_id = None
130- self .minutes_spin .set_sensitive (True )
145+ self .elapsed_time = time .time () - self .start_time # Save the elapsed time
146+ self .minutes_spin .set_sensitive (False ) # Disable minutes_spin when paused
131147 self ._update_start_button_state ()
132148
133149 def _update_timer (self ):
134150 if self .remaining_seconds > 0 :
135- self .remaining_seconds -= 1
151+ self .elapsed_time = time .time () - self .start_time
152+ self .remaining_seconds = self .total_seconds - self .elapsed_time
136153 self ._update_time_label ()
137154 self .progress_circle .queue_draw ()
138- return True
155+ if self .remaining_seconds > 0 :
156+ return True
157+ else :
158+ self ._timer_finished ()
159+ return False
139160 else :
140161 self ._timer_finished ()
141162 return False
142163
143164 def _update_time_label (self ):
144- minutes = self .remaining_seconds // 60
145- seconds = self .remaining_seconds % 60
165+ if self .remaining_seconds < 0 :
166+ self .remaining_seconds = 0
167+ minutes = int (self .remaining_seconds // 60 )
168+ seconds = int (self .remaining_seconds % 60 )
146169 self .time_label .set_label (f"{ minutes :02d} :{ seconds :02d} " )
147170
148171 def _timer_finished (self ):
149172 self .timer_running = False
150173 self .start_button .set_icon_name ("media-playback-start-symbolic" )
151174 self .minutes_spin .set_sensitive (True )
152- self .remaining_seconds = 0
153- self .total_seconds = 0
154- self ._update_time_label ()
155- self .progress_circle .queue_draw ()
156175 self ._update_start_button_state ()
176+ self .elapsed_time = 0 # Reset elapsed time when the timer finishes
177+
178+ def on_close_button_clicked (self , button ):
179+ self .close ()
0 commit comments