From a27494e17dd5c545bdf9d4a5f1a3633b3ba4ad3e Mon Sep 17 00:00:00 2001 From: vsariola <5684185+vsariola@users.noreply.github.com> Date: Wed, 17 Feb 2021 23:30:07 +0200 Subject: [PATCH] feat(tracker): implement alerts that display useful notifications / errors In particular, we show notification after the user has copied something to clipboard (#34) and when there is a patch compile error (#38). --- tracker/alert.go | 116 +++++++++++++++++++++++++++++++++++++++++ tracker/instruments.go | 22 ++++++-- tracker/keyevent.go | 2 + tracker/layout.go | 1 + tracker/songpanel.go | 2 + tracker/theme.go | 2 + tracker/tracker.go | 1 + 7 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 tracker/alert.go diff --git a/tracker/alert.go b/tracker/alert.go new file mode 100644 index 0000000..468ff51 --- /dev/null +++ b/tracker/alert.go @@ -0,0 +1,116 @@ +package tracker + +import ( + "image/color" + "time" + + "gioui.org/f32" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/op/clip" + "gioui.org/op/paint" + "gioui.org/unit" +) + +type Alert struct { + message string + alertType AlertType + duration time.Duration + showMessage string + showAlertType AlertType + showDuration time.Duration + showTime time.Time + pos float64 + lastUpdate time.Time +} + +type AlertType int + +const ( + None AlertType = iota + Notify + Warning + Error +) + +var alertSpeed = 150 * time.Millisecond +var alertMargin = layout.UniformInset(unit.Dp(6)) +var alertInset = layout.UniformInset(unit.Dp(6)) + +func (a *Alert) Update(message string, alertType AlertType, duration time.Duration) { + if a.alertType < alertType { + a.message = message + a.alertType = alertType + a.duration = duration + } +} + +func (a *Alert) Layout(gtx C) D { + now := time.Now() + if a.alertType != None { + a.showMessage = a.message + a.showAlertType = a.alertType + a.showTime = now + a.showDuration = a.duration + } + a.alertType = None + var targetPos float64 = 0.0 + if now.Sub(a.showTime) <= a.showDuration { + targetPos = 1.0 + } + delta := float64(now.Sub(a.lastUpdate)) / float64(alertSpeed) + if a.pos < targetPos { + a.pos += delta + if a.pos > targetPos { + a.pos = targetPos + } else { + op.InvalidateOp{At: now.Add(50 * time.Millisecond)}.Add(gtx.Ops) + } + } else if a.pos > targetPos { + a.pos -= delta + if a.pos < targetPos { + a.pos = targetPos + } else { + op.InvalidateOp{At: now.Add(50 * time.Millisecond)}.Add(gtx.Ops) + } + } + a.lastUpdate = now + var color, textColor, shadeColor color.NRGBA + switch a.showAlertType { + case Warning: + color = warningColor + textColor = black + case Error: + color = errorColor + textColor = black + default: + color = popupSurfaceColor + textColor = white + shadeColor = black + } + bgWidget := func(gtx C) D { + paint.FillShape(gtx.Ops, color, clip.Rect{ + Max: gtx.Constraints.Min, + }.Op()) + return D{Size: gtx.Constraints.Min} + } + labelStyle := LabelStyle{Text: a.showMessage, Color: textColor, ShadeColor: shadeColor, Font: labelDefaultFont, Alignment: layout.Center, FontSize: unit.Dp(16)} + return alertMargin.Layout(gtx, func(gtx C) D { + return layout.S.Layout(gtx, func(gtx C) D { + defer op.Save(gtx.Ops).Load() + gtx.Constraints.Min.X = gtx.Constraints.Max.X + recording := op.Record(gtx.Ops) + dims := layout.Stack{Alignment: layout.Center}.Layout(gtx, + layout.Expanded(bgWidget), + layout.Stacked(func(gtx C) D { + return alertInset.Layout(gtx, labelStyle.Layout) + }), + ) + macro := recording.Stop() + totalY := dims.Size.Y + gtx.Px(alertMargin.Bottom) + op.Offset(f32.Pt(0, float32((1-a.pos)*float64(totalY)))).Add((gtx.Ops)) + macro.Add(gtx.Ops) + return dims + }) + }) +} diff --git a/tracker/instruments.go b/tracker/instruments.go index 0106642..2daa8f0 100644 --- a/tracker/instruments.go +++ b/tracker/instruments.go @@ -1,8 +1,10 @@ package tracker import ( + "fmt" "image" "strconv" + "time" "gioui.org/io/clipboard" "gioui.org/io/pointer" @@ -104,6 +106,7 @@ func (t *Tracker) layoutInstrumentHeader(gtx C) D { contents, err := yaml.Marshal(t.song.Patch.Instruments[t.CurrentInstrument]) if err == nil { clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops) + t.Alert.Update("Instrument copied to clipboard", Notify, time.Second*3) } } for t.DeleteInstrumentBtn.Clicked() { @@ -185,19 +188,20 @@ func (t *Tracker) layoutInstrumentEditor(gtx C) D { addUnitBtnStyle.Background = t.Theme.Fg addUnitBtnStyle.Inset = layout.UniformInset(unit.Dp(4)) - for len(t.StackUse) < len(t.song.Patch.Instruments[t.CurrentInstrument].Units) { + units := t.song.Patch.Instruments[t.CurrentInstrument].Units + for len(t.StackUse) < len(units) { t.StackUse = append(t.StackUse, 0) } stackHeight := 0 - for i, u := range t.song.Patch.Instruments[t.CurrentInstrument].Units { + for i, u := range units { stackHeight += u.StackChange() t.StackUse[i] = stackHeight } element := func(gtx C, i int) D { gtx.Constraints = layout.Exact(image.Pt(gtx.Px(unit.Dp(120)), gtx.Px(unit.Dp(20)))) - u := t.song.Patch.Instruments[t.CurrentInstrument].Units[i] + u := units[i] unitNameLabel := LabelStyle{Text: u.Type, ShadeColor: black, Color: white, Font: labelDefaultFont, FontSize: unit.Sp(12)} if unitNameLabel.Text == "" { unitNameLabel.Text = "---" @@ -210,8 +214,16 @@ func (t *Tracker) layoutInstrumentEditor(gtx C) D { if i > 0 { prevStackUse = t.StackUse[i-1] } - if u.StackNeed() > prevStackUse || (i == len(t.StackUse)-1 && t.StackUse[i] != 0) { + if stackNeed := u.StackNeed(); stackNeed > prevStackUse { unitNameLabel.Color = errorColor + typeString := u.Type + if u.Parameters["stereo"] == 1 { + typeString += " (stereo)" + } + t.Alert.Update(fmt.Sprintf("%v needs at least %v input signals, got %v", typeString, stackNeed, prevStackUse), Error, 0) + } else if i == len(units)-1 && t.StackUse[i] != 0 { + unitNameLabel.Color = warningColor + t.Alert.Update(fmt.Sprintf("Instrument leaves %v signal(s) on the stack", t.StackUse[i]), Warning, 0) } } stackLabel := LabelStyle{Text: stackText, ShadeColor: black, Color: mediumEmphasisTextColor, Font: labelDefaultFont, FontSize: unit.Sp(12)} @@ -224,7 +236,7 @@ func (t *Tracker) layoutInstrumentEditor(gtx C) D { ) } - unitList := FilledDragList(t.Theme, t.UnitDragList, len(t.song.Patch.Instruments[t.CurrentInstrument].Units), element, t.SwapUnits) + unitList := FilledDragList(t.Theme, t.UnitDragList, len(units), element, t.SwapUnits) if t.EditMode == EditUnits { unitList.SelectedColor = cursorColor diff --git a/tracker/keyevent.go b/tracker/keyevent.go index e556482..48b71cc 100644 --- a/tracker/keyevent.go +++ b/tracker/keyevent.go @@ -3,6 +3,7 @@ package tracker import ( "strconv" "strings" + "time" "gioui.org/app" "gioui.org/io/key" @@ -88,6 +89,7 @@ func (t *Tracker) KeyEvent(w *app.Window, e key.Event) bool { contents, err := yaml.Marshal(t.song) if err == nil { w.WriteClipboard(string(contents)) + t.Alert.Update("Song copied to clipboard", Notify, time.Second*3) } return true } diff --git a/tracker/layout.go b/tracker/layout.go index bca30a9..dde8301 100644 --- a/tracker/layout.go +++ b/tracker/layout.go @@ -16,6 +16,7 @@ func (t *Tracker) Layout(gtx layout.Context) { t.VerticalSplit.Layout(gtx, t.layoutTop, t.layoutBottom) + t.Alert.Layout(gtx) } func (t *Tracker) layoutBottom(gtx layout.Context) layout.Dimensions { diff --git a/tracker/songpanel.go b/tracker/songpanel.go index 707353b..2d5509d 100644 --- a/tracker/songpanel.go +++ b/tracker/songpanel.go @@ -4,6 +4,7 @@ import ( "image" "math" "runtime" + "time" "gioui.org/f32" "gioui.org/io/clipboard" @@ -70,6 +71,7 @@ func (t *Tracker) layoutMenuBar(gtx C) D { case 2: if contents, err := yaml.Marshal(t.song); err == nil { clipboard.WriteOp{Text: string(contents)}.Add(gtx.Ops) + t.Alert.Update("Song copied to clipboard", Notify, time.Second*3) } case 3: clipboard.ReadOp{Tag: &t.Menus[1]}.Add(gtx.Ops) diff --git a/tracker/theme.go b/tracker/theme.go index 795a1bc..1d1cf5e 100644 --- a/tracker/theme.go +++ b/tracker/theme.go @@ -71,3 +71,5 @@ var errorColor = color.NRGBA{R: 207, G: 102, B: 121, A: 255} var menuHoverColor = color.NRGBA{R: 30, G: 31, B: 38, A: 255} var scrollBarColor = color.NRGBA{R: 255, G: 255, B: 255, A: 32} + +var warningColor = color.NRGBA{R: 251, G: 192, B: 45, A: 255} diff --git a/tracker/tracker.go b/tracker/tracker.go index fdddb1b..6711929 100644 --- a/tracker/tracker.go +++ b/tracker/tracker.go @@ -83,6 +83,7 @@ type Tracker struct { VerticalSplit *Split StackUse []int KeyPlaying map[string]func() + Alert Alert sequencer *Sequencer refresh chan struct{}