Skip to content

Commit ecce1d7

Browse files
committed
feat: add right-click context menu to timer
- Add OnTappedSecondary support to TappableText widget to handle right-clicks - Implement timer context menu with all available actions: * Play/Pause (with dynamic label based on timer state) * Stop * Next * Settings * Close (hide to tray) * Quit (exit application) - Add Quit option to system tray menu - Left-click continues to toggle play/pause as before - Prepares for future layouts where buttons may be hidden
1 parent 89f38a3 commit ecce1d7

2 files changed

Lines changed: 88 additions & 5 deletions

File tree

ui/tappable_text.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import (
1010

1111
type TappableText struct {
1212
widget.BaseWidget
13-
Label *canvas.Text
14-
OnTapped func()
13+
Label *canvas.Text
14+
OnTapped func()
15+
OnTappedSecondary func(*fyne.PointEvent)
1516
}
1617

1718
func NewTappableText(title string, color color.Color, tapped func()) *TappableText {
@@ -33,6 +34,12 @@ func (t *TappableText) Tapped(*fyne.PointEvent) {
3334
}
3435
}
3536

37+
func (t *TappableText) TappedSecondary(pe *fyne.PointEvent) {
38+
if onTappedSecondary := t.OnTappedSecondary; onTappedSecondary != nil {
39+
onTappedSecondary(pe)
40+
}
41+
}
42+
3643
func (t *TappableText) SetText(text string) {
3744
t.Label.Text = text
3845
t.Refresh()

ui/ui.go

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func Display(app fyne.App, buildInfo BuildInfo, cliStartMinimized bool) {
3030

3131
mainWindow := app.NewWindow("Fynodoro")
3232
mainWindow.SetMaster()
33-
mainWindow.SetContent(MakeClassicLayout(thePomodoro))
33+
mainWindow.SetContent(MakeClassicLayout(app, mainWindow, thePomodoro))
3434
mainWindow.SetCloseIntercept(mainWindow.Hide)
3535
mainWindow.SetFixedSize(true)
3636

@@ -40,7 +40,9 @@ func Display(app fyne.App, buildInfo BuildInfo, cliStartMinimized bool) {
4040
fyne.NewMenuItem("Show", mainWindow.Show),
4141
fyne.NewMenuItem("Hide", mainWindow.Hide),
4242
fyne.NewMenuItem("Center", mainWindow.CenterOnScreen),
43-
fyne.NewMenuItem("About", aboutWindow.Show))
43+
fyne.NewMenuItem("About", aboutWindow.Show),
44+
fyne.NewMenuItemSeparator(),
45+
fyne.NewMenuItem("Quit", func() { app.Quit() }))
4446
desk.SetSystemTrayMenu(trayMenu)
4547
}
4648

@@ -78,7 +80,7 @@ func makeAboutWindow(app fyne.App, buildInfo BuildInfo) fyne.Window {
7880
return aboutWindow
7981
}
8082

81-
func MakeClassicLayout(thePomodoro *pomodoro.Pomodoro) fyne.CanvasObject {
83+
func MakeClassicLayout(app fyne.App, mainWindow fyne.Window, thePomodoro *pomodoro.Pomodoro) fyne.CanvasObject {
8284
timer := NewTappableText(formatDuration(thePomodoro.RemainingTime), nil, nil)
8385
timer.Label.TextSize = 60
8486
timer.Label.TextStyle.Bold = true
@@ -94,6 +96,80 @@ func MakeClassicLayout(thePomodoro *pomodoro.Pomodoro) fyne.CanvasObject {
9496
timer.OnTapped = func() {
9597
playPausePomodoro(thePomodoro, playButton)
9698
}
99+
timer.OnTappedSecondary = func(pe *fyne.PointEvent) {
100+
// Create menu items with dynamic play/pause label
101+
var playPauseLabel string
102+
var playPauseIcon fyne.Resource
103+
if thePomodoro.Running {
104+
playPauseLabel = "Pause"
105+
playPauseIcon = theme.MediaPauseIcon()
106+
} else {
107+
playPauseLabel = "Play"
108+
playPauseIcon = theme.MediaPlayIcon()
109+
}
110+
111+
playPauseItem := fyne.NewMenuItem(playPauseLabel, func() {
112+
playPausePomodoro(thePomodoro, playButton)
113+
})
114+
playPauseItem.Icon = playPauseIcon
115+
116+
stopItem := fyne.NewMenuItem("Stop", func() {
117+
stopPomodoro(thePomodoro, playButton, timer)
118+
})
119+
stopItem.Icon = theme.MediaStopIcon()
120+
121+
nextItem := fyne.NewMenuItem("Next", func() {
122+
nextPomodoro(thePomodoro, playButton, timer)
123+
})
124+
nextItem.Icon = theme.MediaSkipNextIcon()
125+
126+
settingsItem := fyne.NewMenuItem("Settings", func() {
127+
settings := NewSettings()
128+
settings.SetOnSubmit(func() {
129+
// Apply new preferences to current pomodoro
130+
newPref := pref.Load()
131+
thePomodoro.SetWorkDuration(time.Duration(newPref.WorkDuration) * time.Minute)
132+
thePomodoro.SetShortBreakDuration(time.Duration(newPref.ShortBreakDuration) * time.Minute)
133+
thePomodoro.SetLongBreakDuration(time.Duration(newPref.LongBreakDuration) * time.Minute)
134+
thePomodoro.SetWorkRounds(newPref.WorkRounds)
135+
thePomodoro.SetRemainingTime()
136+
137+
// Display new duration
138+
setTimerRemainingTime(thePomodoro, timer)
139+
})
140+
settings.SetOnClosed(func() {
141+
settingsButton.Enable()
142+
})
143+
144+
settingsButton.Disable()
145+
settings.Show()
146+
})
147+
settingsItem.Icon = theme.SettingsIcon()
148+
149+
closeItem := fyne.NewMenuItem("Close", func() {
150+
mainWindow.Hide()
151+
})
152+
closeItem.Icon = theme.WindowCloseIcon()
153+
154+
quitItem := fyne.NewMenuItem("Quit", func() {
155+
app.Quit()
156+
})
157+
quitItem.Icon = theme.CancelIcon()
158+
159+
// Create and show popup menu
160+
menu := fyne.NewMenu("",
161+
playPauseItem,
162+
stopItem,
163+
nextItem,
164+
fyne.NewMenuItemSeparator(),
165+
settingsItem,
166+
fyne.NewMenuItemSeparator(),
167+
closeItem,
168+
quitItem,
169+
)
170+
171+
widget.ShowPopUpMenuAtPosition(menu, mainWindow.Canvas(), pe.AbsolutePosition)
172+
}
97173
playButton.OnTapped = func() {
98174
playPausePomodoro(thePomodoro, playButton)
99175
}

0 commit comments

Comments
 (0)