Skip to content

Commit

Permalink
Show character locations in own tab
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikKalkoken committed Dec 14, 2024
1 parent f1cc1dc commit 6f678a0
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 68 deletions.
3 changes: 1 addition & 2 deletions internal/app/ui/assetsearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,6 @@ func (a *assetSearchArea) characterCount() int {

func (a *assetSearchArea) makeTopText(c int) (string, widget.Importance) {
it := humanize.Comma(int64(len(a.assets)))
ch := humanize.Comma(int64(c))
text := fmt.Sprintf("%s total items - %s characters", it, ch)
text := fmt.Sprintf("%d characters • %s items", c, it)
return text, widget.MediumImportance
}
196 changes: 196 additions & 0 deletions internal/app/ui/locations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package ui

import (
"context"
"fmt"
"log/slog"
"strings"

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

"github.com/ErikKalkoken/evebuddy/internal/app"
"github.com/ErikKalkoken/evebuddy/internal/optional"
"github.com/ErikKalkoken/evebuddy/internal/set"
)

type locationCharacter struct {
id int32
location *app.EntityShort[int64]
name string
region *app.EntityShort[int32]
ship *app.EntityShort[int32]
solarSystem *app.EntityShort[int32]
systemSecurity optional.Optional[float32]
securityImportance widget.Importance
}

// locationsArea is the UI area that shows an overview of all the user's characters.
type locationsArea struct {
characters []locationCharacter
content *fyne.Container
table *widget.Table
top *widget.Label
u *UI
}

func (u *UI) newLocationsArea() *locationsArea {
a := locationsArea{
characters: make([]locationCharacter, 0),
top: widget.NewLabel(""),
u: u,
}
a.top.TextStyle.Bold = true

top := container.NewVBox(a.top, widget.NewSeparator())
a.table = a.makeTable()
a.content = container.NewBorder(top, nil, nil, nil, a.table)
return &a
}

func (a *locationsArea) makeTable() *widget.Table {
var headers = []struct {
text string
maxChars int
}{
{"Name", 20},
{"Location", 30},
{"System", 15},
{"Sec.", 5},
{"Region", 15},
{"Ship", 15},
}

t := widget.NewTable(
func() (rows int, cols int) {
return len(a.characters), len(headers)
},
func() fyne.CanvasObject {
return widget.NewLabel("Template")
},
func(tci widget.TableCellID, co fyne.CanvasObject) {
l := co.(*widget.Label)
if tci.Row >= len(a.characters) || tci.Row < 0 {
return
}
c := a.characters[tci.Row]
l.Alignment = fyne.TextAlignLeading
l.Importance = widget.MediumImportance
l.Truncation = fyne.TextTruncateClip
switch tci.Col {
case 0:
l.Text = c.name
case 1:
l.Text = entityNameOrFallback(c.location, "?")
case 2:
if c.solarSystem == nil || c.systemSecurity.IsEmpty() {
l.Text = "?"
} else {
l.Text = c.solarSystem.Name
}
case 3:
if c.systemSecurity.IsEmpty() {
l.Text = "?"
l.Importance = widget.LowImportance
} else {
l.Text = fmt.Sprintf("%.1f", c.systemSecurity.MustValue())
l.Importance = c.securityImportance
}
l.Alignment = fyne.TextAlignTrailing
case 4:
l.Text = entityNameOrFallback(c.region, "?")
case 5:
l.Text = entityNameOrFallback(c.ship, "?")
}
l.Refresh()
},
)
t.ShowHeaderRow = true
t.StickyColumnCount = 1
t.CreateHeader = func() fyne.CanvasObject {
return widget.NewLabel("Template")
}
t.UpdateHeader = func(tci widget.TableCellID, co fyne.CanvasObject) {
s := headers[tci.Col]
label := co.(*widget.Label)
label.SetText(s.text)
}
t.OnSelected = func(tci widget.TableCellID) {
defer t.UnselectAll()
}

for i, h := range headers {
x := widget.NewLabel(strings.Repeat("w", h.maxChars))
w := x.MinSize().Width
t.SetColumnWidth(i, w)
}
return t
}

func (a *locationsArea) refresh() {
t, i, err := func() (string, widget.Importance, error) {
locations, err := a.updateCharacters()
if err != nil {
return "", 0, err
}
if len(a.characters) == 0 {
return "No characters", widget.LowImportance, nil
}
s := fmt.Sprintf("%d characters • %d locations", len(a.characters), locations)
return s, widget.MediumImportance, nil
}()
if err != nil {
slog.Error("Failed to refresh overview UI", "err", err)
t = "ERROR"
i = widget.DangerImportance
}
a.top.Text = t
a.top.Importance = i
a.table.Refresh()
}

func (a *locationsArea) updateCharacters() (int, error) {
var err error
ctx := context.TODO()
mycc, err := a.u.CharacterService.ListCharacters(ctx)
if err != nil {
return 0, err
}
locationIDs := set.New[int64]()
cc := make([]locationCharacter, len(mycc))
for i, m := range mycc {
c := locationCharacter{
id: m.ID,
name: m.EveCharacter.Name,
}
if m.Location != nil {
locationIDs.Add(m.Location.ID)
c.location = &app.EntityShort[int64]{
ID: m.Location.ID,
Name: m.Location.DisplayName(),
}
if m.Location.SolarSystem != nil {
c.solarSystem = &app.EntityShort[int32]{
ID: m.Location.SolarSystem.ID,
Name: m.Location.SolarSystem.Name,
}
c.systemSecurity = optional.New(m.Location.SolarSystem.SecurityStatus)
c.securityImportance = m.Location.SolarSystem.SecurityType().ToImportance()
c.region = &app.EntityShort[int32]{
ID: m.Location.SolarSystem.Constellation.Region.ID,
Name: m.Location.SolarSystem.Constellation.Region.Name,
}
}
}
if m.Ship != nil {
c.ship = &app.EntityShort[int32]{
ID: m.Ship.ID,
Name: m.Ship.Name,
}
}
cc[i] = c
}
a.characters = cc
return locationIDs.Size(), nil
}
78 changes: 17 additions & 61 deletions internal/app/ui/overview.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,20 @@ import (
)

type overviewCharacter struct {
alliance string
assetValue optional.Optional[float64]
birthday time.Time
corporation string
home *app.EntityShort[int64]
id int32
lastLoginAt optional.Optional[time.Time]
location *app.EntityShort[int64]
name string
region *app.EntityShort[int32]
security float64
ship *app.EntityShort[int32]
solarSystem *app.EntityShort[int32]
systemSecurity optional.Optional[float32]
totalSP optional.Optional[int]
training optional.Optional[time.Duration]
unallocatedSP optional.Optional[int]
unreadCount optional.Optional[int]
walletBalance optional.Optional[float64]
alliance string
assetValue optional.Optional[float64]
birthday time.Time
corporation string
home *app.EntityShort[int64]
id int32
lastLoginAt optional.Optional[time.Time]
name string
security float64
totalSP optional.Optional[int]
training optional.Optional[time.Duration]
unallocatedSP optional.Optional[int]
unreadCount optional.Optional[int]
walletBalance optional.Optional[float64]
}

// overviewArea is the UI area that shows an overview of all the user's characters.
Expand Down Expand Up @@ -77,10 +72,6 @@ func (a *overviewArea) makeTable() *widget.Table {
{"Training", 5},
{"Wallet", 5},
{"Assets", 5},
{"Location", 20},
{"System", 15},
{"Region", 15},
{"Ship", 15},
{"Last Login", 10},
{"Home", 20},
{"Age", 10},
Expand Down Expand Up @@ -140,22 +131,10 @@ func (a *overviewArea) makeTable() *widget.Table {
text = ihumanize.OptionalFloat(c.assetValue, 1, "?")
l.Alignment = fyne.TextAlignTrailing
case 10:
text = entityNameOrFallback(c.location, "?")
case 11:
if c.solarSystem == nil || c.systemSecurity.IsEmpty() {
text = "?"
} else {
text = fmt.Sprintf("%s %.1f", c.solarSystem.Name, c.systemSecurity.MustValue())
}
case 12:
text = entityNameOrFallback(c.region, "?")
case 13:
text = entityNameOrFallback(c.ship, "?")
case 14:
text = ihumanize.Optional(c.lastLoginAt, "?")
case 15:
case 11:
text = entityNameOrFallback(c.home, "?")
case 16:
case 12:
text = humanize.RelTime(c.birthday, time.Now(), "", "")
l.Alignment = fyne.TextAlignTrailing
}
Expand Down Expand Up @@ -200,7 +179,7 @@ func (a *overviewArea) refresh() {
spText := ihumanize.Optional(totals.sp, "?")
unreadText := ihumanize.Optional(totals.unread, "?")
s := fmt.Sprintf(
"Total: %d characters • %s ISK wallet • %s ISK assets • %s SP • %s unread",
"%d characters • %s ISK wallet • %s ISK assets • %s SP • %s unread",
len(a.characters),
walletText,
assetsText,
Expand Down Expand Up @@ -254,29 +233,6 @@ func (a *overviewArea) updateCharacters() (overviewTotals, error) {
Name: m.Home.DisplayName(),
}
}
if m.Location != nil {
c.location = &app.EntityShort[int64]{
ID: m.Location.ID,
Name: m.Location.DisplayName(),
}
if m.Location.SolarSystem != nil {
c.solarSystem = &app.EntityShort[int32]{
ID: m.Location.SolarSystem.ID,
Name: m.Location.SolarSystem.Name,
}
c.systemSecurity = optional.New(m.Location.SolarSystem.SecurityStatus)
c.region = &app.EntityShort[int32]{
ID: m.Location.SolarSystem.Constellation.Region.ID,
Name: m.Location.SolarSystem.Constellation.Region.Name,
}
}
}
if m.Ship != nil {
c.ship = &app.EntityShort[int32]{
ID: m.Ship.ID,
Name: m.Ship.Name,
}
}
cc[i] = c
}
for i, c := range cc {
Expand Down
4 changes: 4 additions & 0 deletions internal/app/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type UI struct {
deskApp desktop.App
fyneApp fyne.App
implantsArea *implantsArea
locationsArea *locationsArea
jumpClonesArea *jumpClonesArea
mailArea *mailArea
mailTab *container.TabItem
Expand Down Expand Up @@ -131,12 +132,14 @@ func NewUI(fyneApp fyne.App, ad appdirs.AppDirs) *UI {
))

u.overviewArea = u.newOverviewArea()
u.locationsArea = u.newLocationsArea()
u.assetSearchArea = u.newAssetSearchArea()
u.coloniesArea = u.newColoniesArea()
u.wealthArea = u.newWealthArea()
u.overviewTab = container.NewTabItemWithIcon("Characters",
theme.NewThemedResource(resourceGroupSvg), container.NewAppTabs(
container.NewTabItem("Overview", u.overviewArea.content),
container.NewTabItem("Locations", u.locationsArea.content),
container.NewTabItem("Assets", u.assetSearchArea.content),
container.NewTabItem("Colonies", u.coloniesArea.content),
container.NewTabItem("Wealth", u.wealthArea.content),
Expand Down Expand Up @@ -425,6 +428,7 @@ func (u *UI) refreshCrossPages() {
ff := map[string]func(){
"assetSearch": u.assetSearchArea.refresh,
"overview": u.overviewArea.refresh,
"locations": u.locationsArea.refresh,
"toolbar": u.toolbarArea.refresh,
"colony": u.coloniesArea.refresh,
"wealth": u.wealthArea.refresh,
Expand Down
13 changes: 8 additions & 5 deletions internal/app/ui/updateticker.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,9 @@ func (u *UI) updateCharacterSectionAndRefreshIfNeeded(ctx context.Context, chara
}
case app.SectionLocation,
app.SectionOnline,
app.SectionShip,
app.SectionWalletBalance:
if hasChanged {
u.overviewArea.refresh()
u.wealthArea.refresh()
app.SectionShip:
if isShown && hasChanged {
u.locationsArea.refresh()
}
case app.SectionPlanets:
if isShown && hasChanged {
Expand Down Expand Up @@ -196,6 +194,11 @@ func (u *UI) updateCharacterSectionAndRefreshIfNeeded(ctx context.Context, chara
if isShown {
u.skillqueueArea.refresh()
}
case app.SectionWalletBalance:
if isShown && hasChanged {
u.overviewArea.refresh()
u.wealthArea.refresh()
}
case app.SectionWalletJournal:
if isShown && hasChanged {
u.walletJournalArea.refresh()
Expand Down

0 comments on commit 6f678a0

Please sign in to comment.