108 lines
3.0 KiB
Go
108 lines
3.0 KiB
Go
package ui
|
|
|
|
import (
|
|
"image"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
"github.com/rivo/tview"
|
|
)
|
|
|
|
const (
|
|
minCoverRows = 6 // a smaller poster is not worth showing
|
|
minCoverCols = 8 //
|
|
minTextRows = 10 // the poster never takes these rows from the text
|
|
)
|
|
|
|
// detailPane shows the details of one title: the head (title and facts) with
|
|
// the poster to its right on top, and the plot below both. On panes too small
|
|
// for the poster everything is one text.
|
|
type detailPane struct {
|
|
*tview.Flex // column: top row, plot
|
|
app *App
|
|
top *tview.Flex // row: head, cover
|
|
head *tview.TextView
|
|
cover *coverView
|
|
plot *tview.TextView
|
|
|
|
text detailText
|
|
want string // key of the poster that should be shown
|
|
hasCover bool // a poster (or its loading note) is part of the content
|
|
combined bool // head and plot are shown together in the plot view
|
|
}
|
|
|
|
func newDetailPane(app *App) *detailPane {
|
|
d := &detailPane{
|
|
Flex: tview.NewFlex().SetDirection(tview.FlexRow),
|
|
app: app,
|
|
top: tview.NewFlex(),
|
|
head: tview.NewTextView().SetDynamicColors(true).SetWordWrap(true),
|
|
cover: newCoverView(app.coverStyle),
|
|
plot: tview.NewTextView().SetDynamicColors(true).SetWordWrap(true),
|
|
}
|
|
d.cover.SetBorderPadding(0, 0, 1, 0)
|
|
d.top.AddItem(d.head, 0, 1, false).AddItem(d.cover, 0, 0, false)
|
|
d.AddItem(d.top, 0, 0, false).AddItem(d.plot, 0, 1, false)
|
|
d.SetBorder(true).SetTitle(" Details ")
|
|
d.combined = true
|
|
return d
|
|
}
|
|
|
|
// Show displays the text and, if there is one, the poster.
|
|
func (d *detailPane) Show(text detailText, ref coverRef) {
|
|
d.text = text
|
|
d.applyText()
|
|
if !d.app.covers || ref.fetch == nil {
|
|
d.want, d.hasCover = "", false
|
|
d.cover.setImage(nil)
|
|
return
|
|
}
|
|
d.want, d.hasCover = ref.key, true
|
|
d.cover.setNote("loading cover…")
|
|
want := ref.key
|
|
d.app.loadCover(ref, func(img *image.RGBA, err error) {
|
|
if d.want != want {
|
|
return // the selection moved on
|
|
}
|
|
if err != nil {
|
|
d.hasCover = false // nothing to show: give the room back to the text
|
|
d.cover.setImage(nil)
|
|
return
|
|
}
|
|
d.cover.setImage(img)
|
|
})
|
|
}
|
|
|
|
// Clear empties the pane.
|
|
func (d *detailPane) Clear() { d.Show(detailText{}, coverRef{}) }
|
|
|
|
// applyText puts the texts into their views for the current layout.
|
|
func (d *detailPane) applyText() {
|
|
if d.combined {
|
|
d.head.SetText("")
|
|
d.plot.SetText(d.text.String()).ScrollToBeginning()
|
|
return
|
|
}
|
|
d.head.SetText(d.text.head).ScrollToBeginning()
|
|
d.plot.SetText(d.text.plot).ScrollToBeginning()
|
|
}
|
|
|
|
func (d *detailPane) Draw(screen tcell.Screen) {
|
|
_, _, w, h := d.GetInnerRect()
|
|
coverW, coverH := 0, 0
|
|
if d.hasCover {
|
|
aw, ah := d.cover.aspect()
|
|
// At most 40% of the width, and the plot keeps its rows.
|
|
coverW, coverH = coverCells(aw, ah, w*2/5, h-minTextRows)
|
|
if coverH < minCoverRows || coverW < minCoverCols {
|
|
coverW, coverH = 0, 0
|
|
}
|
|
}
|
|
if combined := coverW == 0; combined != d.combined {
|
|
d.combined = combined
|
|
d.applyText()
|
|
}
|
|
d.ResizeItem(d.top, coverH, 0)
|
|
d.top.ResizeItem(d.cover, coverW+1, 0) // +1: the padding left of the poster
|
|
d.Flex.Draw(screen)
|
|
}
|