Skip to content

Commit 29c9f0d

Browse files
committed
¬
1 parent 5da8496 commit 29c9f0d

File tree

4 files changed

+281
-21
lines changed

4 files changed

+281
-21
lines changed

main

Whitespace-only changes.

src/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,4 @@ def main(version):
8484
"""The application's entry point."""
8585
app = PytimerApplication()
8686
return app.run(sys.argv)
87+

src/window.py

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# window.py
22
#
3-
# Copyright 2025 Carbon751
3+
# Copyright 2025 code-leech
44
#
55
# This program is free software: you can redistribute it and/or modify
66
# it under the terms of the GNU General Public License as published by
@@ -13,18 +13,166 @@
1313
# GNU General Public License for more details.
1414
#
1515
# You should have received a copy of the GNU General Public License
16-
# along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
#
1818
# SPDX-License-Identifier: GPL-3.0-or-later
1919

20+
import math
21+
import gi
22+
gi.require_version('Gtk', '4.0')
23+
gi.require_version('Adw', '1')
2024
from gi.repository import Adw
2125
from gi.repository import Gtk
26+
from gi.repository import GLib
27+
from gi.repository import Gdk
2228

2329
@Gtk.Template(resource_path='/code/leech/pytimer/window.ui')
2430
class PytimerWindow(Adw.ApplicationWindow):
2531
__gtype_name__ = 'PytimerWindow'
2632

27-
label = Gtk.Template.Child()
33+
# Template children
34+
timer_overlay = Gtk.Template.Child('timer_overlay')
35+
progress_circle = Gtk.Template.Child('progress_circle')
36+
time_label = Gtk.Template.Child('time_label')
37+
start_button = Gtk.Template.Child('start_button')
38+
minutes_spin = Gtk.Template.Child('minutes_spin')
2839

2940
def __init__(self, **kwargs):
3041
super().__init__(**kwargs)
42+
43+
# Initialize timer variables
44+
self.timer_running = False
45+
self.remaining_seconds = 0
46+
self.total_seconds = 0
47+
self.timeout_id = None
48+
49+
# Set up the drawing area
50+
self.progress_circle.set_draw_func(self.draw_timer_arc, None)
51+
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)
55+
56+
# Add custom CSS for styling
57+
css_provider = Gtk.CssProvider()
58+
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+
}
90+
""")
91+
Gtk.StyleContext.add_provider_for_display(
92+
Gdk.Display.get_default(),
93+
css_provider,
94+
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
95+
)
96+
97+
# Set initial time display
98+
self._update_time_label()
99+
100+
def draw_timer_arc(self, area, cr, width, height, data):
101+
"""Draw the circular progress indicator"""
102+
# Calculate center and radius
103+
center_x = width / 2
104+
center_y = height / 2
105+
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
109+
cr.set_line_width(8)
110+
cr.arc(center_x, center_y, radius, 0, 2 * math.pi)
111+
cr.stroke()
112+
113+
if self.total_seconds > 0:
114+
# Draw progress arc with modern color
115+
progress = 1 - (self.remaining_seconds / self.total_seconds)
116+
cr.set_source_rgb(0.2, 0.6, 1.0) # Brighter blue for better visibility
117+
cr.arc(center_x, center_y, radius, -math.pi/2,
118+
2 * math.pi * progress - math.pi/2)
119+
cr.stroke()
120+
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+
134+
def _on_minutes_changed(self, spin_button):
135+
"""Handle minutes spinbutton value changes"""
136+
if not self.timer_running:
137+
minutes = spin_button.get_value_as_int()
138+
self.remaining_seconds = minutes * 60
139+
self.total_seconds = self.remaining_seconds # Update total seconds when minutes change
140+
self._update_time_label()
141+
self.progress_circle.queue_draw()
142+
143+
def _start_timer(self):
144+
"""Start the timer"""
145+
self.timer_running = True
146+
self.timeout_id = GLib.timeout_add_seconds(1, self._update_timer)
147+
self.minutes_spin.set_sensitive(False)
148+
149+
def _stop_timer(self):
150+
"""Stop the timer"""
151+
self.timer_running = False
152+
if self.timeout_id:
153+
GLib.source_remove(self.timeout_id)
154+
self.timeout_id = None
155+
self.minutes_spin.set_sensitive(True)
156+
157+
def _update_timer(self):
158+
"""Update timer state - called every second while timer is running"""
159+
if self.remaining_seconds > 0:
160+
self.remaining_seconds -= 1
161+
self._update_time_label()
162+
self.progress_circle.queue_draw()
163+
return True
164+
else:
165+
self._timer_finished()
166+
return False
167+
168+
def _update_time_label(self):
169+
"""Update the time display label"""
170+
minutes = self.remaining_seconds // 60
171+
seconds = self.remaining_seconds % 60
172+
self.time_label.set_label(f"{minutes:02d}:{seconds:02d}")
173+
174+
def _timer_finished(self):
175+
"""Handle timer completion"""
176+
self.timer_running = False
177+
self.start_button.set_icon_name("media-playback-start-symbolic")
178+
self.minutes_spin.set_sensitive(True)

src/window.ui

Lines changed: 129 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,145 @@
33
<requires lib="gtk" version="4.0"/>
44
<requires lib="Adw" version="1.0"/>
55
<template class="PytimerWindow" parent="AdwApplicationWindow">
6-
<property name="title" translatable="yes">PyTimer</property>
7-
<property name="default-width">800</property>
8-
<property name="default-height">600</property>
9-
<property name="content">
6+
<property name="default-width">400</property>
7+
<property name="default-height">400</property>
8+
<child>
109
<object class="AdwToolbarView">
1110
<child type="top">
12-
<object class="AdwHeaderBar">
13-
<child type="end">
14-
<object class="GtkMenuButton">
15-
<property name="primary">True</property>
16-
<property name="icon-name">open-menu-symbolic</property>
17-
<property name="tooltip-text" translatable="yes">Main Menu</property>
18-
<property name="menu-model">primary_menu</property>
11+
<object class="GtkBox">
12+
<property name="orientation">vertical</property>
13+
<property name="spacing">12</property>
14+
<property name="margin-top">24</property>
15+
<property name="margin-bottom">24</property>
16+
<property name="margin-start">24</property>
17+
<property name="margin-end">24</property>
18+
<property name="vexpand">true</property>
19+
<child>
20+
<object class="AdwClamp">
21+
<property name="maximum-size">300</property>
22+
<child>
23+
<object class="GtkBox">
24+
<property name="orientation">vertical</property>
25+
<property name="spacing">24</property>
26+
<property name="halign">center</property>
27+
<property name="valign">center</property>
28+
<property name="vexpand">true</property>
29+
<child>
30+
<object class="GtkOverlay" id="timer_overlay">
31+
<property name="halign">center</property>
32+
<child type="overlay">
33+
<object class="GtkLabel" id="time_label">
34+
<property name="label">00:00</property>
35+
<style>
36+
<class name="timer-label"/>
37+
</style>
38+
</object>
39+
</child>
40+
<child>
41+
<object class="GtkDrawingArea" id="progress_circle">
42+
<property name="width-request">200</property>
43+
<property name="height-request">200</property>
44+
</object>
45+
</child>
46+
</object>
47+
</child>
48+
<child>
49+
<object class="GtkBox">
50+
<property name="orientation">horizontal</property>
51+
<property name="spacing">12</property>
52+
<property name="halign">center</property>
53+
<child>
54+
<object class="GtkSpinButton" id="minutes_spin">
55+
<property name="valign">center</property>
56+
<property name="adjustment">
57+
<object class="GtkAdjustment">
58+
<property name="lower">0</property>
59+
<property name="upper">59</property>
60+
<property name="step-increment">1</property>
61+
</object>
62+
</property>
63+
<property name="numeric">true</property>
64+
</object>
65+
</child>
66+
<child>
67+
<object class="GtkLabel">
68+
<property name="label">minutes</property>
69+
<style>
70+
<class name="dim-label"/>
71+
</style>
72+
</object>
73+
</child>
74+
</object>
75+
</child>
76+
</object>
77+
</child>
1978
</object>
2079
</child>
2180
</object>
2281
</child>
23-
<property name="content">
24-
<object class="GtkLabel" id="label">
25-
<property name="label" translatable="yes">Hello, World!</property>
82+
<child type="bottom">
83+
<object class="GtkBox">
2684
<style>
27-
<class name="title-1"/>
85+
<class name="toolbar"/>
2886
</style>
87+
<child>
88+
<object class="GtkWindowHandle">
89+
<property name="vexpand">false</property>
90+
<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>
135+
</object>
136+
</child>
137+
</object>
138+
</child>
29139
</object>
30-
</property>
140+
</child>
31141
</object>
32-
</property>
142+
</child>
33143
</template>
144+
34145
<menu id="primary_menu">
35146
<section>
36147
<item>
@@ -42,7 +153,7 @@
42153
<attribute name="action">win.show-help-overlay</attribute>
43154
</item>
44155
<item>
45-
<attribute name="label" translatable="yes">_About PyTimer</attribute>
156+
<attribute name="label" translatable="yes">_About Timer</attribute>
46157
<attribute name="action">app.about</attribute>
47158
</item>
48159
</section>

0 commit comments

Comments
 (0)