Skip to content

Commit 3a9ef0d

Browse files
committed
draw a legend on the left side
1 parent 0816b23 commit 3a9ef0d

File tree

2 files changed

+92
-39
lines changed

2 files changed

+92
-39
lines changed

core/core.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,10 @@ func (g BandGraph) ElapsedTimePercent(timestamp time.Time) float64 {
725725
return float64(g.ElapsedTime(timestamp)) / float64(g.duration)
726726
}
727727

728+
func (g BandGraph) PercentAsDuration(percent float64) time.Duration {
729+
return time.Duration(float64(g.duration) * percent)
730+
}
731+
728732
type BandScore struct {
729733
QSOs int
730734
Duplicates int

ui/scoreGraph.go

Lines changed: 88 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ui
33
import (
44
"fmt"
55
"math"
6+
"time"
67

78
"github.com/gotk3/gotk3/cairo"
89
"github.com/gotk3/gotk3/gtk"
@@ -128,8 +129,8 @@ type graphLayout struct {
128129
multisLowZoneHeight float64
129130
binWidth float64
130131
divX []float64
131-
axisWidth float64
132-
divisionWidth float64
132+
axisLineWidth float64
133+
divisionLineWidth float64
133134
leftLegendWidth float64
134135
timeIndicatorHeight float64
135136
}
@@ -139,7 +140,6 @@ func (g *scoreGraph) Draw(da *gtk.DrawingArea, cr *cairo.Context) {
139140
defer cr.Restore()
140141

141142
// preparations
142-
143143
valueCount := 0
144144
if len(g.graphs) > 0 {
145145
valueCount = len(g.graphs[0].DataPoints)
@@ -169,32 +169,33 @@ func (g *scoreGraph) Draw(da *gtk.DrawingArea, cr *cairo.Context) {
169169

170170
func (g *scoreGraph) calculateLayout(da *gtk.DrawingArea, cr *cairo.Context, valueCount int) graphLayout {
171171
result := graphLayout{
172-
width: float64(da.GetAllocatedWidth()),
173-
height: float64(da.GetAllocatedHeight()),
174-
marginY: 10.0,
175-
axisWidth: 1.0,
176-
divisionWidth: .5,
172+
width: float64(da.GetAllocatedWidth()),
173+
height: float64(da.GetAllocatedHeight()),
174+
marginY: 10.0,
175+
axisLineWidth: 1.0,
176+
divisionLineWidth: .5,
177177
}
178178

179179
cr.SetFontSize(g.style.fontSize)
180180
result.leftLegendWidth = cr.TextExtents("00:00").Width + 2.0
181181
result.timeIndicatorHeight = cr.TextExtents("Hg").Height + 2.0
182+
graphWidth := result.width - result.leftLegendWidth
182183

183184
result.zeroY = (result.height - result.timeIndicatorHeight) / 2.0
184185
result.maxHeight = result.zeroY - result.marginY
185186
result.pointsLowZoneHeight = math.Min(result.maxHeight/2.0, (result.maxHeight/float64(g.maxPoints))*g.pointsBinGoal)
186187
result.multisLowZoneHeight = math.Min(result.maxHeight/2.0, (result.maxHeight/float64(g.maxMultis))*g.multisBinGoal)
187188
if valueCount > 0 {
188-
result.binWidth = result.width / float64(valueCount)
189+
result.binWidth = graphWidth / float64(valueCount)
189190
} else {
190-
result.binWidth = result.width
191+
result.binWidth = graphWidth
191192
}
192193

193194
const divCount = 8
194195
if len(result.divX) != divCount {
195196
result.divX = make([]float64, divCount-1)
196197
}
197-
divWidth := result.width / float64(divCount)
198+
divWidth := graphWidth / float64(divCount)
198199
for i := range result.divX {
199200
result.divX[i] = float64(i+1) * divWidth
200201
}
@@ -211,32 +212,60 @@ func (g *scoreGraph) fillBackground(cr *cairo.Context) {
211212
}
212213

213214
func (g *scoreGraph) drawZeroLine(cr *cairo.Context, layout graphLayout) {
215+
// the line
214216
cr.SetSourceRGB(g.style.axisColor.ToRGB())
215-
cr.SetLineWidth(layout.axisWidth)
216-
cr.MoveTo(0, layout.zeroY)
217+
cr.SetLineWidth(layout.axisLineWidth)
218+
cr.MoveTo(layout.leftLegendWidth, layout.zeroY)
217219
cr.LineTo(layout.width, layout.zeroY)
218220
cr.Stroke()
221+
222+
// the legend
223+
g.drawYLegendAt(cr, layout, layout.zeroY, "0")
224+
}
225+
226+
func (g *scoreGraph) drawYLegendAt(cr *cairo.Context, layout graphLayout, y float64, text string) {
227+
textExtents := cr.TextExtents(text)
228+
left := layout.leftLegendWidth - textExtents.Width - 2.0
229+
bottom := y + textExtents.Height/2.0
230+
cr.SetSourceRGB(g.style.axisColor.ToRGB())
231+
cr.SetFontSize(g.style.fontSize)
232+
cr.MoveTo(left, bottom)
233+
cr.ShowText(text)
219234
}
220235

221236
func (g *scoreGraph) drawTimeDivisions(cr *cairo.Context, layout graphLayout) {
222237
cr.SetSourceRGB(g.style.axisColor.ToRGB())
223-
cr.SetLineWidth(layout.divisionWidth)
238+
cr.SetLineWidth(layout.divisionLineWidth)
239+
cr.SetFontSize(g.style.fontSize)
240+
241+
// the zero line
242+
cr.MoveTo(layout.leftLegendWidth, layout.zeroY-layout.maxHeight)
243+
cr.LineTo(layout.leftLegendWidth, layout.zeroY+layout.maxHeight)
244+
cr.Stroke()
245+
246+
// the vertical divisions
224247
for _, x := range layout.divX {
225-
cr.MoveTo(x, layout.zeroY-layout.maxHeight)
226-
cr.LineTo(x, layout.zeroY+layout.maxHeight)
248+
cr.MoveTo(x+layout.leftLegendWidth, layout.zeroY-layout.maxHeight)
249+
cr.LineTo(x+layout.leftLegendWidth, layout.zeroY+layout.maxHeight)
227250
cr.Stroke()
228251
}
229252
}
230253

231254
func (g *scoreGraph) drawTimeIndicator(cr *cairo.Context, layout graphLayout) {
232-
if g.timeFrameIndex <= 0 {
233-
return
234-
}
235255
now := g.clock.Now()
236256

257+
var elapsedTime time.Duration
258+
var elapsedTimePercent float64
259+
if g.timeFrameIndex >= 0 && len(g.graphs) > 0 {
260+
elapsedTime = g.graphs[0].ElapsedTime(now)
261+
elapsedTimePercent = g.graphs[0].ElapsedTimePercent(now)
262+
} else {
263+
elapsedTime = 0
264+
elapsedTimePercent = 0.0
265+
}
266+
237267
// the time bar
238-
elapsedTimePercent := g.graphs[0].ElapsedTimePercent(now)
239-
left := 0.0
268+
left := layout.leftLegendWidth
240269
right := left + (layout.width-left)*elapsedTimePercent
241270
bottom := layout.height - layout.marginY
242271
top := bottom - layout.timeIndicatorHeight
@@ -250,45 +279,61 @@ func (g *scoreGraph) drawTimeIndicator(cr *cairo.Context, layout graphLayout) {
250279
cr.Fill()
251280

252281
// the elapsed time
253-
elapsedTime := g.graphs[0].ElapsedTime(now)
254-
elapsedTimeText := fmt.Sprintf("%02d:%02d", int(elapsedTime.Hours()), int(elapsedTime.Minutes())%60)
282+
elapsedTimeText := formatDuration(elapsedTime)
255283

256284
cr.SetSourceRGB(g.style.axisColor.ToRGB())
257285
cr.SetFontSize(g.style.fontSize)
258286
cr.MoveTo(1, layout.height-layout.marginY-1)
259287
cr.ShowText(elapsedTimeText)
260288

261-
// the old box
262-
startX := float64(g.timeFrameIndex) * layout.binWidth
263-
endX := float64(g.timeFrameIndex+1) * layout.binWidth
289+
// the time legend
290+
for i, x := range layout.divX {
291+
if i%2 == 1 && len(g.graphs) > 0 {
292+
percent := float64(i+1) / float64(len(layout.divX)+1)
293+
text := formatDuration(g.graphs[0].PercentAsDuration(percent))
294+
textExtents := cr.TextExtents(text)
295+
cr.MoveTo(x+layout.leftLegendWidth-textExtents.Width/2.0, layout.zeroY+layout.maxHeight+textExtents.Height+2)
296+
cr.ShowText(text)
297+
}
298+
}
264299

265-
cr.SetSourceRGBA(g.style.timeFrameColor.ToRGBA())
266-
cr.SetLineWidth(layout.divisionWidth)
267-
cr.MoveTo(startX, layout.zeroY-layout.maxHeight)
268-
cr.LineTo(endX, layout.zeroY-layout.maxHeight)
269-
cr.LineTo(endX, layout.zeroY+layout.maxHeight)
270-
cr.LineTo(startX, layout.zeroY+layout.maxHeight)
271-
cr.ClosePath()
272-
cr.Stroke()
300+
// the old box
301+
if g.timeFrameIndex >= 0 {
302+
startX := float64(g.timeFrameIndex)*layout.binWidth + layout.leftLegendWidth
303+
endX := float64(g.timeFrameIndex+1)*layout.binWidth + layout.leftLegendWidth
304+
305+
cr.SetSourceRGBA(g.style.timeFrameColor.ToRGBA())
306+
cr.SetLineWidth(layout.divisionLineWidth)
307+
cr.MoveTo(startX, layout.zeroY-layout.maxHeight)
308+
cr.LineTo(endX, layout.zeroY-layout.maxHeight)
309+
cr.LineTo(endX, layout.zeroY+layout.maxHeight)
310+
cr.LineTo(startX, layout.zeroY+layout.maxHeight)
311+
cr.ClosePath()
312+
cr.Stroke()
313+
}
273314
}
274315

275316
func (g *scoreGraph) drawLowZone(cr *cairo.Context, layout graphLayout) {
276317
cr.SetSourceRGBA(g.style.lowZoneColor.WithAlpha(g.style.areaAlpha))
277-
cr.MoveTo(0, layout.zeroY-layout.pointsLowZoneHeight)
318+
cr.MoveTo(layout.leftLegendWidth, layout.zeroY-layout.pointsLowZoneHeight)
278319
cr.LineTo(layout.width, layout.zeroY-layout.pointsLowZoneHeight)
279320
cr.LineTo(layout.width, layout.zeroY+layout.multisLowZoneHeight)
280-
cr.LineTo(0, layout.zeroY+layout.multisLowZoneHeight)
321+
cr.LineTo(layout.leftLegendWidth, layout.zeroY+layout.multisLowZoneHeight)
281322
cr.ClosePath()
282323
cr.Fill()
283324

284325
cr.SetSourceRGBA(g.style.lowZoneColor.WithAlpha(g.style.borderAlpha))
285-
cr.SetLineWidth(layout.divisionWidth)
286-
cr.MoveTo(0, layout.zeroY-layout.pointsLowZoneHeight)
326+
cr.SetLineWidth(layout.divisionLineWidth)
327+
cr.MoveTo(layout.leftLegendWidth, layout.zeroY-layout.pointsLowZoneHeight)
287328
cr.LineTo(layout.width, layout.zeroY-layout.pointsLowZoneHeight)
288329
cr.LineTo(layout.width, layout.zeroY+layout.multisLowZoneHeight)
289-
cr.LineTo(0, layout.zeroY+layout.multisLowZoneHeight)
330+
cr.LineTo(layout.leftLegendWidth, layout.zeroY+layout.multisLowZoneHeight)
290331
cr.ClosePath()
291332
cr.Stroke()
333+
334+
// the legend
335+
g.drawYLegendAt(cr, layout, layout.zeroY-layout.pointsLowZoneHeight, fmt.Sprintf("%d", int(g.pointsGoal)))
336+
g.drawYLegendAt(cr, layout, layout.zeroY+layout.multisLowZoneHeight, fmt.Sprintf("%d", int(g.multisGoal)))
292337
}
293338

294339
func (g *scoreGraph) drawDataPointsRectangular(cr *cairo.Context, layout graphLayout, datapoints []core.BandScore) {
@@ -397,3 +442,7 @@ func (g *scoreGraph) drawDataPointsCurved(cr *cairo.Context, layout graphLayout,
397442
cr.ClosePath()
398443
cr.Fill()
399444
}
445+
446+
func formatDuration(d time.Duration) string {
447+
return fmt.Sprintf("%02d:%02d", int(d.Hours()), int(d.Minutes())%60)
448+
}

0 commit comments

Comments
 (0)