Skip to content

Commit

Permalink
show the qsos with fyne
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Thienel <[email protected]>
  • Loading branch information
ftl committed Dec 8, 2024
1 parent 924cf4b commit 6ef9c73
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 16 deletions.
20 changes: 17 additions & 3 deletions core/logbook/qsolist.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,12 +338,10 @@ func (l *QSOList) GetQSOs(numbers []core.QSONumber) []core.QSO {
func (l *QSOList) getQSOs(numbers []core.QSONumber) []core.QSO {
result := make([]core.QSO, 0, len(numbers))
for _, n := range numbers {
listIndex, found := l.findIndex(n)
qso, found := l.getQSO(n)
if !found {
log.Printf("QSO number %d not found", n)
continue
}
qso := l.list[listIndex]
if len(result) > 0 && n > result[len(result)-1].MyNumber {
result = append(result, qso)
} else {
Expand All @@ -357,6 +355,22 @@ func (l *QSOList) getQSOs(numbers []core.QSONumber) []core.QSO {
return result
}

func (l *QSOList) GetQSO(number core.QSONumber) (core.QSO, bool) {
l.dataLock.RLock()
defer l.dataLock.RUnlock()

return l.getQSO(number)
}

func (l *QSOList) getQSO(number core.QSONumber) (core.QSO, bool) {
listIndex, found := l.findIndex(number)
if !found {
log.Printf("QSO number %d not found", number)
return core.QSO{}, false
}
return l.list[listIndex], true
}

func (l *QSOList) FindWorkedQSOs(callsign callsign.Callsign, band core.Band, mode core.Mode) ([]core.QSO, bool) {
l.dataLock.RLock()

Expand Down
9 changes: 8 additions & 1 deletion fyneui/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type application struct {
shortcuts *Shortcuts
mainWindow *mainWindow
mainMenu *mainMenu
qsoList *qsoList
statusBar *statusBar

controller *app.Controller
Expand All @@ -49,17 +50,23 @@ func (a *application) activate() {
a.controller.Startup()

a.shortcuts = setupShortcuts(a.controller)
a.qsoList = setupQSOList()
a.statusBar = setupStatusBar()

mainWindow := a.app.NewWindow("Hello Contest")
a.mainWindow = setupMainWindow(mainWindow, a.statusBar)
a.mainWindow = setupMainWindow(mainWindow, a.qsoList, a.statusBar)
a.shortcuts.AddTo(mainWindow.Canvas())

a.mainMenu = setupMainMenu(a.mainWindow.window, a.controller, a.shortcuts)

a.qsoList.SetLogbookController(a.controller.QSOList)

a.controller.SetView(a.mainWindow)
a.controller.QSOList.Notify(a.qsoList)
a.controller.ServiceStatus.Notify(a.statusBar)

a.controller.Refresh()

a.mainWindow.UseDefaultWindowGeometry() // TODO: store/restore the window geometry
a.mainWindow.Show()
}
Expand Down
13 changes: 6 additions & 7 deletions fyneui/mainWindow.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,24 @@ import (
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/storage"
"fyne.io/fyne/v2/widget"
)

type mainWindow struct {
window fyne.Window
}

func setupMainWindow(window fyne.Window, statusBar *statusBar) *mainWindow {
func setupMainWindow(window fyne.Window, qsoList *qsoList, statusBar *statusBar) *mainWindow {
result := &mainWindow{
window: window,
}
window.SetMaster()

root := container.NewBorder(
nil, // top
statusBar.container, // bottom
nil, // left
nil, // right
widget.NewLabel("Hello Contest"), // center
nil, // top
statusBar.container, // bottom
nil, // left
nil, // right
qsoList.container, // center
)
window.SetContent(root)

Expand Down
204 changes: 204 additions & 0 deletions fyneui/qsoList.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package fyneui

import (
"fmt"
"time"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"

"github.com/ftl/hellocontest/core"
)

const (
exchangeAt = 4
myExchangeTemplate = "My %s"
theirExchangeTemplate = "Th %s"
exchangeColumnWidth = 100
)

var columnHeaders = []string{"UTC", "Callsign", "Band", "Mode", "Pts", "Mult", "D"}
var columnWidth = []float32{55, 100, 55, 50, 35, 40, 10}

type LogbookController interface {
GetExchangeFields() ([]core.ExchangeField, []core.ExchangeField)
SelectRow(int)
}

type qsoList struct {
container *fyne.Container
table *widget.Table
controller LogbookController

myExchangeFields []core.ExchangeField
theirExchangeFields []core.ExchangeField

headerRow []string
entryRows [][]string
}

func setupQSOList() *qsoList {
result := &qsoList{
headerRow: columnHeaders,
entryRows: [][]string{},
}

result.table = widget.NewTable(result.tableSize, result.createTableCell, result.updateValueCell)
result.table.ShowHeaderRow = true
result.table.CreateHeader = result.createTableCell
result.table.UpdateHeader = result.updateHeaderCell

result.container = container.New(layout.NewStackLayout(), result.table)

return result
}

func (l *qsoList) SetLogbookController(controller LogbookController) {
l.controller = controller
l.ExchangeFieldsChanged(l.controller.GetExchangeFields())
}

func (l *qsoList) updateHeaderRow() {
exchangeLength := len(l.myExchangeFields) + len(l.theirExchangeFields)
firstTheirExchange := exchangeAt + len(l.myExchangeFields)
length := len(columnHeaders) + exchangeLength

headerRow := make([]string, length)
for i := range headerRow {
if i < exchangeAt {
headerRow[i] = columnHeaders[i]
} else if i >= exchangeAt+exchangeLength {
headerRow[i] = columnHeaders[i-exchangeLength]
} else if i < firstTheirExchange {
headerRow[i] = fmt.Sprintf(myExchangeTemplate, exchangeColumnName(l.myExchangeFields[i-exchangeAt]))
} else {
headerRow[i] = fmt.Sprintf(theirExchangeTemplate, exchangeColumnName(l.theirExchangeFields[i-firstTheirExchange]))
}
}

l.headerRow = headerRow
}

func exchangeColumnName(field core.ExchangeField) string {
if len(field.Properties) == 1 {
return field.Short
}
return "Exch"
}

func (l *qsoList) tableSize() (int, int) {
return len(l.entryRows), len(l.headerRow)
}

func (l *qsoList) createTableCell() fyne.CanvasObject {
return widget.NewLabel("")
}

func (l *qsoList) updateHeaderCell(id widget.TableCellID, cell fyne.CanvasObject) {
label := cell.(*widget.Label)
label.TextStyle.Bold = true
label.SetText(l.columnHeaderText(id.Col))

l.table.SetColumnWidth(id.Col, l.columnWidth(id.Col))
}

func (l *qsoList) columnHeaderText(column int) string {
if column < 0 || column >= len(l.headerRow) {
return ""
}

return l.headerRow[column]
}

func (l *qsoList) columnWidth(column int) float32 {
if column < exchangeAt {
return columnWidth[column]
} else if column >= len(l.headerRow)-exchangeAt {
return columnWidth[column-exchangeAt]
} else {
return exchangeColumnWidth
}
}

func (l *qsoList) updateValueCell(id widget.TableCellID, cell fyne.CanvasObject) {
label := cell.(*widget.Label)
label.TextStyle.Bold = false
label.SetText(l.valueCellText(id.Row, id.Col))
}

func (l *qsoList) valueCellText(row int, column int) string {
if row < 0 || row >= len(l.entryRows) {
return ""
}
entryRow := l.entryRows[row]
if column < 0 || column >= len(entryRow) {
return ""
}

return entryRow[column]
}

func (l *qsoList) QSOsCleared() {
l.entryRows = [][]string{}
}

func (l *qsoList) QSOAdded(qso core.QSO) {
entryRow := l.qsoToRow(qso)
l.entryRows = append(l.entryRows, entryRow)
}

func (l *qsoList) qsoToRow(qso core.QSO) []string {
length := len(l.headerRow)
firstTheirExchange := exchangeAt + len(l.myExchangeFields)
result := make([]string, length)
result[0] = qso.Time.In(time.UTC).Format("15:04")
result[1] = qso.Callsign.String()
result[2] = qso.Band.String()
result[3] = qso.Mode.String()
result[length-3] = pointsToString(qso.Points, qso.Duplicate)
result[length-2] = pointsToString(qso.Multis, qso.Duplicate)
result[length-1] = boolToCheckmark(qso.Duplicate)

for i, value := range qso.MyExchange {
result[i+exchangeAt] = value
}

for i, value := range qso.TheirExchange {
result[i+firstTheirExchange] = value
}

return result
}

func pointsToString(points int, duplicate bool) string {
if duplicate {
return fmt.Sprintf("(%d)", points)
}
return fmt.Sprintf("%d", points)
}

func boolToCheckmark(value bool) string {
if value {
return "✓"
}
return ""
}

func (l *qsoList) forRow(row int, f func(widget.TableCellID)) {
for col := range l.headerRow {
id := widget.TableCellID{Row: row, Col: col}
f(id)
}
}

func (l *qsoList) RowSelected(row int) {
l.table.Select(widget.TableCellID{Row: row, Col: 0})
}

func (l *qsoList) ExchangeFieldsChanged(myExchangeFields []core.ExchangeField, theirExchangeFields []core.ExchangeField) {
l.myExchangeFields = myExchangeFields
l.theirExchangeFields = theirExchangeFields
l.updateHeaderRow()
}
5 changes: 0 additions & 5 deletions ui/logbookView.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,6 @@ func boolToCheckmark(value bool) string {
return ""
}

func (v *logbookView) QSOInserted(index int, qso core.QSO) {
// insertion is currently not supported as it does not happen in practice
log.Printf("qso %d inserted at %d", qso.MyNumber, index)
}

func (v *logbookView) QSOUpdated(index int, _, qso core.QSO) {
row, err := v.list.GetIterFromString(fmt.Sprintf("%d", index))
if err != nil {
Expand Down

0 comments on commit 6ef9c73

Please sign in to comment.