Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
789ee25e1f refactor: another ui test 2025-06-27 20:42:17 +02:00
4 changed files with 182 additions and 86 deletions

View file

@ -16,8 +16,7 @@ import (
func main() { func main() {
myApp := app.NewWithID("com.example.kettlebell-tracker") myApp := app.NewWithID("com.example.kettlebell-tracker")
// myApp.Settings().SetTheme(theme.DarkTheme()) mainWindow := myApp.NewWindow("Kettlebell Programm Tracker")
mainWIndow := myApp.NewWindow("Kettlebell Programm Tracker")
dbDir := myApp.Storage().RootURI().Path() dbDir := myApp.Storage().RootURI().Path()
dbPath := filepath.Join(dbDir, "kb_training.db") dbPath := filepath.Join(dbDir, "kb_training.db")
@ -32,23 +31,50 @@ func main() {
apiService := services.NewApiService(myApp.UniqueID()) apiService := services.NewApiService(myApp.UniqueID())
trainingService := services.NewTrainingService(dbService, settingsService, apiService) trainingService := services.NewTrainingService(dbService, settingsService, apiService)
// Dark Mode nach Systemeinstellung
if fyne.CurrentDevice().IsMobile() {
myApp.Settings().SetTheme(theme.DarkTheme())
}
homeScreen := ui.MakeHomeScreen() // mainWindow := myApp.NewWindow("Kettlebell Tracker")
settingsScreen := ui.MakeSettingsScreen(settingsService, mainWIndow) mainWindow.SetMaster()
historyScreen := ui.MakeHistoryScreen(dbService, mainWIndow)
trainingScreen := ui.MakeTrainingScreen(trainingService, settingsService, mainWIndow)
tabs := container.NewAppTabs( // Responsive Layout
container.NewTabItemWithIcon("Home", theme.HomeIcon(), homeScreen), content := container.NewMax()
container.NewTabItemWithIcon("Training", theme.MediaPlayIcon(), trainingScreen), nav := buildNavigation(content, mainWindow, dbService, settingsService, apiService, trainingService) // Eigene Nav-Komponente
container.NewTabItemWithIcon("Historie", theme.HistoryIcon(), historyScreen),
container.NewTabItemWithIcon("Einstellungen", theme.SettingsIcon(), settingsScreen),
)
tabs.SetTabLocation(container.TabLocationBottom) mainWindow.SetContent(container.NewBorder(nav, nil, nil, nil, content))
mainWindow.Resize(fyne.NewSize(400, 600))
mainWIndow.Resize(fyne.NewSize(400, 600)) mainWindow.ShowAndRun()
mainWIndow.SetContent(tabs) }
mainWIndow.SetMaster()
mainWIndow.ShowAndRun() func buildNavigation(content *fyne.Container, mainWindow fyne.Window, dbService *data.DatabaseService, settingsService *services.SettingsService, apiService *services.ApiService, trainingService *services.TrainingService) fyne.CanvasObject {
navItems := []struct {
icon fyne.Resource
title string
view func() fyne.CanvasObject
}{
{theme.HomeIcon(), "Home", ui.MakeHomeScreen},
{theme.MediaPlayIcon(), "Training", func() fyne.CanvasObject {
return ui.MakeTrainingScreen(trainingService, settingsService, mainWindow)
}},
{theme.HistoryIcon(), "Historie", func() fyne.CanvasObject {
return ui.MakeHistoryScreen(dbService, mainWindow)
}},
{theme.SettingsIcon(), "Einstellungen", func() fyne.CanvasObject {
return ui.MakeSettingsScreen(settingsService, mainWindow)
}},
}
nav := container.NewAppTabs()
for _, item := range navItems {
nav.Append(container.NewTabItemWithIcon(item.title, item.icon, item.view()))
}
nav.SetTabLocation(container.TabLocationBottom)
nav.OnSelected = func(t *container.TabItem) {
content.Objects = []fyne.CanvasObject{t.Content}
content.Refresh()
}
return nav
} }

View file

@ -84,9 +84,36 @@ func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.Canvas
refreshData() refreshData()
// Toolbar mit Refresh-Button // Toolbar mit Refresh-Button
toolbar := widget.NewToolbar( // toolbar := widget.NewToolbar(
widget.NewToolbarAction(theme.ViewRefreshIcon(), refreshData), // widget.NewToolbarAction(theme.ViewRefreshIcon(), refreshData),
// )
table := widget.NewTable(
func() (int, int) { return len(history), 4 },
func() fyne.CanvasObject { return widget.NewLabel("Template") },
func(id widget.TableCellID, cell fyne.CanvasObject) {
session := history[id.Row]
label := cell.(*widget.Label)
switch id.Col {
case 0:
label.SetText(session.Date.Format("02.01"))
case 1:
label.SetText(fmt.Sprintf("%d Sätze", session.Sets))
case 2:
label.SetText(fmt.Sprintf("%.1fkg", session.WeightLeft))
case 3:
label.SetText(formatDuration(session.Duration))
}
},
) )
table.SetColumnWidth(0, 80)
table.SetColumnWidth(1, 100)
table.SetColumnWidth(2, 80)
table.SetColumnWidth(3, 80)
return container.NewBorder(toolbar, nil, nil, nil, container.NewStack(list, placeholder)) // return container.NewBorder(toolbar, nil, nil, nil, container.NewStack(list, placeholder))
return container.NewBorder(
widget.NewToolbar(widget.NewToolbarAction(theme.ViewRefreshIcon(), refreshData)),
nil, nil, nil,
table,
)
} }

View file

@ -8,29 +8,69 @@ import (
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
) )
// MakeHomeScreen erstellt den statischen Willkommensbildschirm. // // MakeHomeScreen erstellt den statischen Willkommensbildschirm.
// func MakeHomeScreen() fyne.CanvasObject {
// primaryColor := theme.PrimaryColor()
//
// title := canvas.NewText("Willkommen beim Giant Programm Tracker!", primaryColor)
// title.TextStyle.Bold = true
// title.Alignment = fyne.TextAlignCenter
// title.TextSize = 24
//
// subtitle := widget.NewLabel("Verwalte deine Kettlebell-Trainings effizient.")
// subtitle.Alignment = fyne.TextAlignCenter
//
// icon := widget.NewIcon(theme.MediaPlayIcon())
// icon.Resize(fyne.NewSize(150, 150))
//
// // Layout erstellen, das dem Flutter-Layout entspricht
// content := container.NewVBox(
// title,
// widget.NewSeparator(),
// subtitle,
// container.NewPadded(icon), // Icon mit etwas Abstand
// )
//
// // Zentriert den Inhalt
// return container.NewCenter(content)
// }
func MakeHomeScreen() fyne.CanvasObject { func MakeHomeScreen() fyne.CanvasObject {
primaryColor := theme.PrimaryColor() primary := theme.PrimaryColor()
secondary := theme.WarningColor()
title := canvas.NewText("Willkommen beim Giant Programm Tracker!", primaryColor) // Header mit personalisierter Begrüßung
title.TextStyle.Bold = true greeting := canvas.NewText("Dein Kettlebell Fortschritt", primary)
title.Alignment = fyne.TextAlignCenter greeting.TextSize = 20
title.TextSize = 24 greeting.Alignment = fyne.TextAlignCenter
subtitle := widget.NewLabel("Verwalte deine Kettlebell-Trainings effizient.") // Fortschrittsvisualisierung (Beispiel)
subtitle.Alignment = fyne.TextAlignCenter progressRing := canvas.NewCircle(secondary)
progressRing.StrokeWidth = 8
progressRing.StrokeColor = primary
ringContainer := container.NewCenter(progressRing)
icon := widget.NewIcon(theme.MediaPlayIcon()) // Programmstatus
icon.Resize(fyne.NewSize(150, 150)) programInfo := widget.NewLabel("Aktuell: Giant - Woche 2/4")
programInfo.Alignment = fyne.TextAlignCenter
// Layout erstellen, das dem Flutter-Layout entspricht // Primäre Aktion
content := container.NewVBox( startBtn := widget.NewButtonWithIcon("Training starten", theme.MediaPlayIcon(), func() {})
title, startBtn.Importance = widget.HighImportance
return container.NewVBox(
container.NewPadded(greeting),
ringContainer,
programInfo,
container.NewCenter(startBtn),
widget.NewSeparator(), widget.NewSeparator(),
subtitle, buildQuickStats(), // Eigene Komponente für Statistiken
container.NewPadded(icon), // Icon mit etwas Abstand )
}
func buildQuickStats() fyne.CanvasObject {
return container.NewGridWithColumns(2,
widget.NewCard("Letztes Training", "12 Sätze", nil),
widget.NewCard("Aktuelle Serie", "5 Tage", nil),
) )
// Zentriert den Inhalt
return container.NewCenter(content)
} }

View file

@ -9,6 +9,7 @@ import (
"git.patanix.de/git/kettlebell-app/internal/services" "git.patanix.de/git/kettlebell-app/internal/services"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
@ -75,35 +76,35 @@ func MakeTrainingScreen(ts *services.TrainingService, ss *services.SettingsServi
} }
} }
startAction := func() { // startAction := func() {
settings := ss.LoadSettings() // settings := ss.LoadSettings()
ts.StartTraining(settings.TrainingTimeMinutes, settings.GoalSets) // ts.StartTraining(settings.TrainingTimeMinutes, settings.GoalSets)
updateUI() // updateUI()
//
mainTimer = time.NewTicker(time.Second) // mainTimer = time.NewTicker(time.Second)
go func() { // go func() {
for range mainTimer.C { // for range mainTimer.C {
if ts.State.RemainingSeconds <= 0 { // if ts.State.RemainingSeconds <= 0 {
stopTimers() // stopTimers()
fyne.CurrentApp().SendNotification(&fyne.Notification{ // fyne.CurrentApp().SendNotification(&fyne.Notification{
Title: "Zeit abgelaufen!", // Title: "Zeit abgelaufen!",
Content: "Training wird automatisch gespeichert.", // Content: "Training wird automatisch gespeichert.",
}) // })
finishButton.OnTapped() // finishButton.OnTapped()
return // return
} // }
ts.Tick() // ts.Tick()
updateUI() // updateUI()
} // }
}() // }()
//
lastSetTimer = time.NewTicker(time.Second) // lastSetTimer = time.NewTicker(time.Second)
go func() { // go func() {
for range lastSetTimer.C { // for range lastSetTimer.C {
ts.TickLastSetTimer() // ts.TickLastSetTimer()
} // }
}() // }()
} // }
setAction := func() { setAction := func() {
ts.CompleteSet() ts.CompleteSet()
@ -135,27 +136,29 @@ func MakeTrainingScreen(ts *services.TrainingService, ss *services.SettingsServi
updateUI() updateUI()
} }
startButton = widget.NewButtonWithIcon("Start", theme.MediaPlayIcon(), startAction) timerDisplay := canvas.NewText("00:00", theme.PrimaryColor())
setButton = widget.NewButtonWithIcon("Satz", theme.ConfirmIcon(), setAction) timerDisplay.TextSize = 48
finishButton = widget.NewButtonWithIcon("Beenden", theme.MediaStopIcon(), finishAction) timerDisplay.Alignment = fyne.TextAlignCenter
updateUI() exerciseInfo := widget.NewLabel("Aktuelle Übung: Double Clean & Press")
exerciseInfo.Alignment = fyne.TextAlignCenter
headerCard := widget.NewCard("", "", container.NewVBox(programLabel, blockDayLabel, repsLabel)) progressRing := canvas.NewCircle(theme.BackgroundColor())
timerCard := widget.NewCard("", "", container.NewVBox( progressRing.StrokeWidth = 5
widget.NewLabelWithStyle("Verbleibende Zeit", fyne.TextAlignCenter, fyne.TextStyle{}), progressRing.StrokeColor = theme.PrimaryColor()
timerLabel,
progressBar,
progressLabel,
))
actionButtons := container.NewGridWithColumns(3, startButton, setButton, finishButton) // Vereinfachte Steuerung
historyCard := widget.NewCard("Satz-Historie", "", setHistoryList) setBtn := widget.NewButtonWithIcon("Satz", theme.ConfirmIcon(), setAction)
finishBtn := widget.NewButtonWithIcon("Beenden", theme.CancelIcon(), finishAction)
actionBar := container.NewGridWithColumns(2, setBtn, finishBtn)
return container.NewVBox( // Tastatur-Shortcut
headerCard, canvasObj := container.NewVBox(
timerCard, container.NewCenter(timerDisplay),
actionButtons, container.NewCenter(exerciseInfo),
historyCard, container.NewCenter(progressRing),
actionBar,
) )
return container.NewPadded(canvasObj)
} }