Maj branch
This commit is contained in:
116
gui.go
116
gui.go
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -16,7 +17,9 @@ import (
|
||||
// ── Modèle TableView ──────────────────────────────────────────────────────────
|
||||
|
||||
type RepoItem struct {
|
||||
result RepoResult
|
||||
result RepoResult
|
||||
progress float64 // 0.0 à 1.0
|
||||
progressText string // ex: "Réception 45% (1.2 Go/2.5 Go)"
|
||||
}
|
||||
|
||||
func (it *RepoItem) statusText() string {
|
||||
@@ -49,6 +52,27 @@ func (it *RepoItem) statusText() string {
|
||||
return msg
|
||||
}
|
||||
|
||||
// progressBarText génère une barre de progression visuelle en Unicode.
|
||||
// Ex: "████████░░░░ 67% Réception objets"
|
||||
func progressBarText(pct float64, width int, label string) string {
|
||||
if width <= 0 {
|
||||
width = 20
|
||||
}
|
||||
filled := int(pct * float64(width))
|
||||
if filled > width {
|
||||
filled = width
|
||||
}
|
||||
bar := strings.Repeat("█", filled) + strings.Repeat("░", width-filled)
|
||||
pctInt := int(pct * 100)
|
||||
if pctInt > 100 {
|
||||
pctInt = 100
|
||||
}
|
||||
if label != "" {
|
||||
return fmt.Sprintf("%s %3d%% %s", bar, pctInt, label)
|
||||
}
|
||||
return fmt.Sprintf("%s %3d%%", bar, pctInt)
|
||||
}
|
||||
|
||||
func (it *RepoItem) textColor() walk.Color {
|
||||
r := it.result
|
||||
if r.Pending {
|
||||
@@ -94,28 +118,54 @@ func (m *RepoModel) Value(row, col int) interface{} {
|
||||
return ""
|
||||
}
|
||||
it := m.items[row]
|
||||
if col == 0 {
|
||||
switch col {
|
||||
case 0:
|
||||
return it.result.Name
|
||||
case 1:
|
||||
return it.statusText()
|
||||
case 2:
|
||||
return it.progressText
|
||||
}
|
||||
return it.statusText()
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *RepoModel) StyleCell(style *walk.CellStyle) {
|
||||
if style.Col() != 1 {
|
||||
return
|
||||
}
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if style.Row() >= len(m.items) {
|
||||
row := style.Row()
|
||||
if row >= len(m.items) {
|
||||
return
|
||||
}
|
||||
style.TextColor = m.items[style.Row()].textColor()
|
||||
col := style.Col()
|
||||
if col == 1 {
|
||||
style.TextColor = m.items[row].textColor()
|
||||
}
|
||||
if col == 2 {
|
||||
it := m.items[row]
|
||||
if it.progress > 0 && it.progress < 1.0 {
|
||||
style.TextColor = walk.RGB(0, 100, 180)
|
||||
} else if it.progress >= 1.0 {
|
||||
style.TextColor = walk.RGB(0, 150, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *RepoModel) setResult(row int, res RepoResult) {
|
||||
m.mu.Lock()
|
||||
if row < len(m.items) {
|
||||
m.items[row].result = res
|
||||
m.items[row].progress = 0
|
||||
m.items[row].progressText = ""
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.PublishRowChanged(row)
|
||||
}
|
||||
|
||||
func (m *RepoModel) setProgress(row int, pct float64, text string) {
|
||||
m.mu.Lock()
|
||||
if row < len(m.items) {
|
||||
m.items[row].progress = pct
|
||||
m.items[row].progressText = text
|
||||
}
|
||||
m.mu.Unlock()
|
||||
m.PublishRowChanged(row)
|
||||
@@ -184,8 +234,8 @@ func (a *App) buildAndRun() error {
|
||||
if err := (MainWindow{
|
||||
AssignTo: &a.mw,
|
||||
Title: "Git Update Checker v" + VERSION,
|
||||
MinSize: Size{Width: 650, Height: 400},
|
||||
Size: Size{Width: 820, Height: 600},
|
||||
MinSize: Size{Width: 750, Height: 400},
|
||||
Size: Size{Width: 950, Height: 600},
|
||||
Layout: VBox{Margins: Margins{Left: 10, Top: 10, Right: 10, Bottom: 10}},
|
||||
Children: []Widget{
|
||||
// En-tête
|
||||
@@ -208,8 +258,9 @@ func (a *App) buildAndRun() error {
|
||||
AlternatingRowBG: true,
|
||||
ColumnsOrderable: false,
|
||||
Columns: []TableViewColumn{
|
||||
{Title: "Dépôt", Width: 180},
|
||||
{Title: "Statut", Width: 350},
|
||||
{Title: "Dépôt", Width: 150},
|
||||
{Title: "Statut", Width: 200},
|
||||
{Title: "Progression", Width: 350},
|
||||
},
|
||||
Model: a.model,
|
||||
OnCurrentIndexChanged: a.onSelectionChanged,
|
||||
@@ -382,6 +433,31 @@ func logLineForResult(r RepoResult) string {
|
||||
return msg
|
||||
}
|
||||
|
||||
// ── Progression ───────────────────────────────────────────────────────────────
|
||||
|
||||
// makeProgressCB crée un callback de progression pour la ligne row du tableau.
|
||||
// Le callback est appelé depuis un goroutine git et synchronise l'UI via mw.Synchronize.
|
||||
func (a *App) makeProgressCB(row int) ProgressCallback {
|
||||
// Limiter les mises à jour UI (max ~10/s) pour ne pas surcharger
|
||||
var lastUpdate time.Time
|
||||
return func(info ProgressInfo) {
|
||||
now := time.Now()
|
||||
if now.Sub(lastUpdate) < 100*time.Millisecond && info.Percent < 1.0 {
|
||||
return
|
||||
}
|
||||
lastUpdate = now
|
||||
|
||||
label := info.Phase
|
||||
if info.Speed != "" {
|
||||
label += " " + info.Speed
|
||||
}
|
||||
text := progressBarText(info.Percent, 20, label)
|
||||
a.mw.Synchronize(func() {
|
||||
a.model.setProgress(row, info.Percent, text)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ── Actions dépôt ─────────────────────────────────────────────────────────────
|
||||
|
||||
func (a *App) onSelectionChanged() {
|
||||
@@ -413,23 +489,26 @@ func (a *App) doAction() {
|
||||
a.btnAction.SetEnabled(false)
|
||||
a.appendLog(fmt.Sprintf("[%s] Mise à jour en cours...", res.Name))
|
||||
|
||||
cb := a.makeProgressCB(idx)
|
||||
go func() {
|
||||
var err error
|
||||
if res.NeedsClone {
|
||||
err = doClone(cfg)
|
||||
err = doCloneWithProgress(cfg, cb)
|
||||
} else {
|
||||
if res.LocalChanges > 0 {
|
||||
err = doCheckout(res)
|
||||
}
|
||||
if err == nil && res.NewCommits > 0 {
|
||||
err = doPull(res)
|
||||
err = doPullWithProgress(res, cb)
|
||||
}
|
||||
}
|
||||
a.mw.Synchronize(func() {
|
||||
if err != nil {
|
||||
a.model.setProgress(idx, 0, "Erreur")
|
||||
walk.MsgBox(a.mw, "Erreur", res.Name+"\n\n"+err.Error(), walk.MsgBoxIconError)
|
||||
logError(fmt.Sprintf("[%s] %v", res.Name, err))
|
||||
} else {
|
||||
a.model.setProgress(idx, 1.0, progressBarText(1.0, 20, "Terminé"))
|
||||
a.appendLog(fmt.Sprintf("[%s] Mise à jour OK", res.Name))
|
||||
logInfo(fmt.Sprintf("[%s] Mise à jour OK", res.Name))
|
||||
}
|
||||
@@ -449,24 +528,27 @@ func (a *App) updateAll() {
|
||||
continue
|
||||
}
|
||||
pending.Add(1)
|
||||
cfg, res := cfg, res
|
||||
i, cfg, res := i, cfg, res
|
||||
cb := a.makeProgressCB(i)
|
||||
go func() {
|
||||
var err error
|
||||
if res.NeedsClone {
|
||||
err = doClone(cfg)
|
||||
err = doCloneWithProgress(cfg, cb)
|
||||
} else {
|
||||
if res.LocalChanges > 0 {
|
||||
err = doCheckout(res)
|
||||
}
|
||||
if err == nil && res.NewCommits > 0 {
|
||||
err = doPull(res)
|
||||
err = doPullWithProgress(res, cb)
|
||||
}
|
||||
}
|
||||
a.mw.Synchronize(func() {
|
||||
if err != nil {
|
||||
a.model.setProgress(i, 0, "Erreur")
|
||||
logError(fmt.Sprintf("[%s] %v", res.Name, err))
|
||||
a.appendLog(fmt.Sprintf("[%s] Erreur: %v", res.Name, err))
|
||||
} else {
|
||||
a.model.setProgress(i, 1.0, progressBarText(1.0, 20, "Terminé"))
|
||||
logInfo(fmt.Sprintf("[%s] Mise à jour OK", res.Name))
|
||||
a.appendLog(fmt.Sprintf("[%s] Mise à jour OK", res.Name))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user