Skip to content

Commit 0b844f6

Browse files
committed
¬
1 parent 29c9f0d commit 0b844f6

File tree

3 files changed

+98
-144
lines changed

3 files changed

+98
-144
lines changed

src/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,4 @@ def main(version):
8585
app = PytimerApplication()
8686
return app.run(sys.argv)
8787

88+

src/window.py

Lines changed: 69 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,8 @@
1-
# window.py
2-
#
3-
# Copyright 2025 code-leech
4-
#
5-
# This program is free software: you can redistribute it and/or modify
6-
# it under the terms of the GNU General Public License as published by
7-
# the Free Software Foundation, either version 3 of the License, or
8-
# (at your option) any later version.
9-
#
10-
# This program is distributed in the hope that it will be useful,
11-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-
# GNU General Public License for more details.
14-
#
15-
# You should have received a copy of the GNU General Public License
16-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17-
#
18-
# SPDX-License-Identifier: GPL-3.0-or-later
19-
201
import math
212
import gi
223
gi.require_version('Gtk', '4.0')
234
gi.require_version('Adw', '1')
24-
from gi.repository import Adw
25-
from gi.repository import Gtk
26-
from gi.repository import GLib
27-
from gi.repository import Gdk
5+
from gi.repository import Adw, Gtk, GLib, Gdk
286

297
@Gtk.Template(resource_path='/code/leech/pytimer/window.ui')
308
class PytimerWindow(Adw.ApplicationWindow):
@@ -34,8 +12,9 @@ class PytimerWindow(Adw.ApplicationWindow):
3412
timer_overlay = Gtk.Template.Child('timer_overlay')
3513
progress_circle = Gtk.Template.Child('progress_circle')
3614
time_label = Gtk.Template.Child('time_label')
37-
start_button = Gtk.Template.Child('start_button')
3815
minutes_spin = Gtk.Template.Child('minutes_spin')
16+
header_bar = Gtk.Template.Child('header_bar')
17+
button_box = Gtk.Template.Child('button_box')
3918

4019
def __init__(self, **kwargs):
4120
super().__init__(**kwargs)
@@ -46,116 +25,112 @@ def __init__(self, **kwargs):
4625
self.total_seconds = 0
4726
self.timeout_id = None
4827

49-
# Set up the drawing area
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)
34+
self.start_button.connect('clicked', self._on_start_clicked)
35+
self.button_box.append(self.start_button)
36+
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)
43+
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)
46+
47+
# Set up drawing area
5048
self.progress_circle.set_draw_func(self.draw_timer_arc, None)
5149

52-
# Connect signals using connect_after to ensure widget is fully initialized
53-
self.start_button.connect_after('clicked', self._on_start_clicked)
54-
self.minutes_spin.connect_after('value-changed', self._on_minutes_changed)
50+
# Connect signals
51+
self.minutes_spin.connect('value-changed', self._on_minutes_changed)
5552

56-
# Add custom CSS for styling
53+
# Add CSS for styling
5754
css_provider = Gtk.CssProvider()
5855
css_provider.load_from_data(b"""
59-
.timer-label {
60-
font-size: 48px;
61-
font-weight: 300;
62-
}
63-
64-
.timer-circle {
65-
margin: 24px;
66-
}
67-
68-
headerbar.tall {
69-
min-height: 64px;
70-
}
71-
72-
button.circular {
73-
padding: 8px;
74-
margin: 4px;
75-
border-radius: 9999px;
76-
}
77-
78-
button.closebutton {
79-
padding: 0;
80-
margin: 0;
81-
min-width: 24px;
82-
min-height: 24px;
83-
background-color: #ebebeb;
84-
border-radius: 9999px;
85-
}
86-
87-
button.closebutton:hover {
88-
background-color: #d4d4d4;
89-
}
56+
.timer-label { font-size: 48px; font-weight: 300; }
57+
button.circular { padding: 4px; margin: 2px; border-radius: 9999px; }
58+
headerbar { padding: 0; min-height: 24px; box-shadow: none; border-bottom: none; }
59+
window { margin: 0; }
9060
""")
9161
Gtk.StyleContext.add_provider_for_display(
9262
Gdk.Display.get_default(),
9363
css_provider,
9464
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
9565
)
9666

97-
# Set initial time display
67+
# Initial setup
68+
self._update_time_label()
69+
self._update_start_button_state()
70+
71+
def _on_reset_clicked(self, button):
72+
self.remaining_seconds = 0
73+
self.total_seconds = 0
9874
self._update_time_label()
75+
self.reset_button.set_visible(False)
76+
self.progress_circle.queue_draw()
77+
78+
def _on_start_clicked(self, button):
79+
if not self.timer_running:
80+
if self.total_seconds == 0:
81+
minutes = self.minutes_spin.get_value_as_int()
82+
if minutes > 0:
83+
self.total_seconds = minutes * 60
84+
self.remaining_seconds = self.total_seconds
85+
self._start_timer()
86+
button.set_icon_name("media-playback-stop-symbolic")
87+
else:
88+
self._stop_timer()
89+
button.set_icon_name("media-playback-start-symbolic")
90+
if self.remaining_seconds > 0:
91+
self.reset_button.set_visible(True)
9992

10093
def draw_timer_arc(self, area, cr, width, height, data):
101-
"""Draw the circular progress indicator"""
102-
# Calculate center and radius
10394
center_x = width / 2
10495
center_y = height / 2
10596
radius = min(width, height) / 2 - 10
106-
107-
# Draw background circle with modern styling
108-
cr.set_source_rgba(0.9, 0.9, 0.9, 0.2) # More subtle background
97+
cr.set_source_rgba(0.9, 0.9, 0.9, 0.2)
10998
cr.set_line_width(8)
11099
cr.arc(center_x, center_y, radius, 0, 2 * math.pi)
111100
cr.stroke()
112-
113101
if self.total_seconds > 0:
114-
# Draw progress arc with modern color
102+
cr.set_source_rgb(0.2, 0.6, 1.0)
115103
progress = 1 - (self.remaining_seconds / self.total_seconds)
116-
cr.set_source_rgb(0.2, 0.6, 1.0) # Brighter blue for better visibility
117104
cr.arc(center_x, center_y, radius, -math.pi/2,
118-
2 * math.pi * progress - math.pi/2)
105+
2 * math.pi * progress - math.pi/2)
119106
cr.stroke()
120107

121-
def _on_start_clicked(self, button):
122-
"""Handle start/stop button clicks"""
123-
if not self.timer_running:
124-
minutes = self.minutes_spin.get_value_as_int()
125-
if minutes > 0:
126-
self.total_seconds = minutes * 60
127-
self.remaining_seconds = self.total_seconds
128-
self._start_timer()
129-
button.set_icon_name("media-playback-stop-symbolic")
130-
else:
131-
self._stop_timer()
132-
button.set_icon_name("media-playback-start-symbolic")
133-
134108
def _on_minutes_changed(self, spin_button):
135-
"""Handle minutes spinbutton value changes"""
136109
if not self.timer_running:
137110
minutes = spin_button.get_value_as_int()
138111
self.remaining_seconds = minutes * 60
139-
self.total_seconds = self.remaining_seconds # Update total seconds when minutes change
112+
self.total_seconds = self.remaining_seconds
140113
self._update_time_label()
141-
self.progress_circle.queue_draw()
114+
self._update_start_button_state()
115+
116+
def _update_start_button_state(self):
117+
minutes = self.minutes_spin.get_value_as_int()
118+
self.start_button.set_sensitive(minutes > 0 or self.remaining_seconds > 0)
142119

143120
def _start_timer(self):
144-
"""Start the timer"""
145121
self.timer_running = True
146122
self.timeout_id = GLib.timeout_add_seconds(1, self._update_timer)
147123
self.minutes_spin.set_sensitive(False)
148124

149125
def _stop_timer(self):
150-
"""Stop the timer"""
151126
self.timer_running = False
152127
if self.timeout_id:
153128
GLib.source_remove(self.timeout_id)
154129
self.timeout_id = None
155130
self.minutes_spin.set_sensitive(True)
131+
self._update_start_button_state()
156132

157133
def _update_timer(self):
158-
"""Update timer state - called every second while timer is running"""
159134
if self.remaining_seconds > 0:
160135
self.remaining_seconds -= 1
161136
self._update_time_label()
@@ -166,13 +141,16 @@ def _update_timer(self):
166141
return False
167142

168143
def _update_time_label(self):
169-
"""Update the time display label"""
170144
minutes = self.remaining_seconds // 60
171145
seconds = self.remaining_seconds % 60
172146
self.time_label.set_label(f"{minutes:02d}:{seconds:02d}")
173147

174148
def _timer_finished(self):
175-
"""Handle timer completion"""
176149
self.timer_running = False
177150
self.start_button.set_icon_name("media-playback-start-symbolic")
178151
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()
156+
self._update_start_button_state()

src/window.ui

Lines changed: 28 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
<template class="PytimerWindow" parent="AdwApplicationWindow">
66
<property name="default-width">400</property>
77
<property name="default-height">400</property>
8+
<property name="title" translatable="yes">PyTimer</property>
89
<child>
910
<object class="AdwToolbarView">
11+
<property name="hexpand">true</property>
12+
<property name="vexpand">true</property>
1013
<child type="top">
1114
<object class="GtkBox">
1215
<property name="orientation">vertical</property>
1316
<property name="spacing">12</property>
14-
<property name="margin-top">24</property>
15-
<property name="margin-bottom">24</property>
17+
<property name="margin-top">30</property>
1618
<property name="margin-start">24</property>
1719
<property name="margin-end">24</property>
1820
<property name="vexpand">true</property>
@@ -80,61 +82,34 @@
8082
</object>
8183
</child>
8284
<child type="bottom">
83-
<object class="GtkBox">
84-
<style>
85-
<class name="toolbar"/>
86-
</style>
87-
<child>
88-
<object class="GtkWindowHandle">
89-
<property name="vexpand">false</property>
85+
<object class="AdwHeaderBar" id="header_bar">
86+
<property name="hexpand">true</property>
87+
<property name="title-widget">
88+
<object class="GtkBox">
89+
<property name="orientation">horizontal</property>
90+
<property name="halign">center</property>
91+
<property name="valign">end</property>
92+
<property name="margin-bottom">2</property>
9093
<child>
91-
<object class="GtkHeaderBar">
92-
<property name="show-title-buttons">false</property>
93-
<child type="start">
94-
<object class="GtkBox">
95-
<style>
96-
<class name="linked"/>
97-
</style>
98-
</object>
99-
</child>
100-
<child type="center">
101-
<object class="GtkButton" id="start_button">
102-
<property name="icon-name">media-playback-start-symbolic</property>
103-
<style>
104-
<class name="circular"/>
105-
<class name="flat"/>
106-
</style>
107-
</object>
108-
</child>
109-
<child type="end">
110-
<object class="GtkBox">
111-
<property name="spacing">6</property>
112-
<child>
113-
<object class="GtkMenuButton">
114-
<property name="icon-name">open-menu-symbolic</property>
115-
<property name="menu-model">primary_menu</property>
116-
<style>
117-
<class name="flat"/>
118-
</style>
119-
</object>
120-
</child>
121-
<child>
122-
<object class="GtkButton">
123-
<property name="action-name">app.quit</property>
124-
<property name="icon-name">window-close-symbolic</property>
125-
<property name="width-request">24</property>
126-
<property name="height-request">24</property>
127-
<style>
128-
<class name="circular"/>
129-
<class name="closebutton"/>
130-
</style>
131-
</object>
132-
</child>
133-
</object>
134-
</child>
94+
<object class="GtkBox" id="button_box">
95+
<property name="orientation">horizontal</property>
96+
<property name="spacing">6</property>
13597
</object>
13698
</child>
13799
</object>
100+
</property>
101+
<child type="start">
102+
<object class="GtkMenuButton">
103+
<property name="valign">center</property>
104+
<property name="margin-start">6</property>
105+
<property name="margin-bottom">2</property>
106+
<property name="icon-name">open-menu-symbolic</property>
107+
<property name="menu-model">primary_menu</property>
108+
<style>
109+
<class name="circular"/>
110+
<class name="flat"/>
111+
</style>
112+
</object>
138113
</child>
139114
</object>
140115
</child>

0 commit comments

Comments
 (0)