browse indexed tags

This commit is contained in:
ron 2020-03-18 02:41:33 +01:00
parent d0bff1d1bc
commit 9af9e33bad
6 changed files with 268 additions and 64 deletions

61
main.go
View File

@ -6,6 +6,7 @@ import (
"os"
"os/user"
"path/filepath"
"sort"
"talc/pkg/menu"
"talc/pkg/tal"
@ -18,7 +19,6 @@ var path = flag.String("path", ".", "library path")
func init() {
flag.Parse()
*path = filepath.FromSlash(*path)
if (*path)[0] == '~' {
user, err := user.Current()
@ -44,15 +44,15 @@ func (t *Talc) HandleEvent(ev tcell.Event) bool {
case tcell.KeyCtrlC:
app.Quit()
return true
case tcell.KeyCtrlL:
app.Refresh()
return true
case tcell.KeyRune:
switch ev.Rune() {
case 'q':
app.Quit()
return true
}
case tcell.KeyCtrlL:
app.Refresh()
return true
}
case *tcell.EventResize:
app.Refresh()
@ -74,21 +74,52 @@ func main() {
os.Exit(11)
}
books := make([]string, len(alib.Books))
for i, book := range alib.Books {
books[i] = book.String()
}
s := tcell.StyleDefault
ss := s.Foreground(tcell.ColorGreen)
m := menu.NewMenu()
m.SetStyle(s)
m.SetSelectedStyle(ss)
m.EnableCursor(true)
m.SetLines(books)
meta := alib.GetMeta()
talc.AddWidget(m, 1)
root := menu.Option{
Name: "talc",
}
tags := []menu.Option{}
for tag, vals := range meta {
topt := menu.Option{
Name: tag,
Action: menu.ActionMenu,
}
valopts := []menu.Option{}
for value, books := range vals {
vopt := menu.Option{
Name: value,
Action: menu.ActionMenu,
}
bookopts := []menu.Option{}
for _, book := range books {
bopt := menu.Option{
Name: book.String(),
Action: menu.ActionExec,
Args: []string{os.Getenv("EDITOR"), book.Path},
}
bookopts = append(bookopts, bopt)
}
sort.Sort(menu.ByName(bookopts))
vopt.Options = bookopts
valopts = append(valopts, vopt)
}
sort.Sort(menu.ByOptions(valopts))
topt.Options = valopts
tags = append(tags, topt)
}
sort.Sort(menu.ByOptions(tags))
root.Options = tags
mt := menu.NewMenu()
mt.SetStyle(s)
mt.SetSelectedStyle(ss)
mt.SetCurrent(root)
talc.AddWidget(mt, 10)
app.SetRootWidget(talc)
if err := app.Run(); err != nil {

View File

@ -12,20 +12,36 @@ type Menu struct {
MenuView
}
func (m *Menu) SetLines(lines []string) {
// func (m *Menu) SetLines(lines []string) {
// m.Init()
// mm := m.model
// mm.width = 0
// mm.height = len(lines)
// mm.lines = lines
// for _, l := range lines {
// if len(l) > mm.width {
// mm.width = len(l)
// }
// }
// m.MenuView.SetModel(mm)
// }
func (m *Menu) SetCurrent(option Option) {
m.Init()
mm := m.model
mm.width = 0
mm.height = len(lines)
mm.lines = lines
for _, l := range lines {
if len(l) > mm.width {
mm.width = len(l)
}
}
mm.SetCurrent(option)
m.MenuView.SetModel(mm)
}
// func (m *Menu) SetOptions(options []Option) {
// m.Init()
// mm := m.model
// mm.SetOptions(options)
// m.MenuView.SetModel(mm)
// }
func (m *Menu) SetStyle(style tcell.Style) {
m.model.style = style
m.MenuView.SetStyle(style)
@ -48,7 +64,6 @@ func (m *Menu) HideCursor(on bool) {
func (m *Menu) Init() {
m.once.Do(func() {
mm := &menuModel{
lines: []string{},
width: 0,
cursor: true,
hide: false,

View File

@ -6,14 +6,20 @@ import (
type MenuModel interface {
GetBounds() (int, int)
SetCursor(int)
GetCurrent() Option
//GetParent() *Option
GetCursor() (int, bool, bool)
MoveCursor(offx, offy int)
GetLine(int) (string, tcell.Style)
GetOption(int) Option
MoveCursor(y int)
SetCurrent(Option)
//SetParent(*Option)
SetCursor(int)
}
type menuModel struct {
lines []string
//parent *Option
current Option
width int
height int
y int
@ -23,17 +29,52 @@ type menuModel struct {
style tcell.Style
}
func (m *menuModel) GetLine(y int) (string, tcell.Style) {
if y < 0 || y >= len(m.lines) {
return "", m.style
}
return m.lines[y], m.style
}
func (m *menuModel) GetBounds() (int, int) {
return m.width, m.height
}
// func (m *menuModel) GetParent() *Option {
// return m.parent
// }
// func (m *menuModel) SetParent(opt *Option) {
// m.parent = opt
// }
func (m *menuModel) GetCurrent() Option {
return m.current
}
func (m *menuModel) SetCurrent(opt Option) {
m.current = opt
m.y = opt.y
m.height = len(m.current.Options)
m.width = 0
for _, opt := range m.current.Options {
if len(opt.Name) > m.width {
m.width = len(opt.Name)
}
}
return
}
func (m *menuModel) SetCursor(y int) {
m.y = y
m.limitCursor()
}
func (m *menuModel) GetOption(y int) Option {
if y < 0 || y >= len(m.current.Options) {
return Option{}
}
return m.current.Options[y]
}
func (m *menuModel) MoveCursor(y int) {
m.y += y
m.limitCursor()
}
func (m *menuModel) limitCursor() {
if m.y > m.height-1 {
m.y = m.height - 1
@ -43,16 +84,6 @@ func (m *menuModel) limitCursor() {
}
}
func (m *menuModel) SetCursor(y int) {
m.y = y
m.limitCursor()
}
func (m *menuModel) MoveCursor(x, y int) {
m.y += y
m.limitCursor()
}
func (m *menuModel) GetCursor() (int, bool, bool) {
return m.y, m.cursor, !m.hide
}

39
pkg/menu/menu_option.go Normal file
View File

@ -0,0 +1,39 @@
package menu
import "fmt"
const (
ActionMenu = iota + 1
ActionExec
)
type Action uint8
type Option struct {
Name string
Options []Option
Action Action
Args []string
y int
parent *Option
}
func (o *Option) String() string {
ol := len(o.Options)
if ol > 1 {
return fmt.Sprintf("%s (%d)", o.Name, ol)
}
return o.Name
}
type ByName []Option
func (a ByName) Len() int { return len(a) }
func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
type ByOptions []Option
func (a ByOptions) Len() int { return len(a) }
func (a ByOptions) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByOptions) Less(i, j int) bool { return len(a[i].Options) > len(a[j].Options) }

View File

@ -1,6 +1,8 @@
package menu
import (
"os"
"os/exec"
"sync"
"github.com/gdamore/tcell"
@ -16,6 +18,7 @@ type MenuView struct {
model MenuModel
view views.View
port *views.ViewPort
views.WidgetWatchers
once sync.Once
}
@ -27,6 +30,12 @@ func (mv *MenuView) HandleEvent(e tcell.Event) bool {
switch e := e.(type) {
case *tcell.EventKey:
switch e.Key() {
case tcell.KeyBackspace, tcell.KeyBackspace2:
mv.keyBack()
return true
case tcell.KeyEnter:
mv.keyEnter()
return true
case tcell.KeyUp, tcell.KeyCtrlP:
mv.keyUp()
return true
@ -47,18 +56,32 @@ func (mv *MenuView) HandleEvent(e tcell.Event) bool {
return true
case tcell.KeyRune:
switch e.Rune() {
case 'h':
mv.keyBack()
return true
case 'j':
mv.keyDown()
return true
case 'k':
mv.keyUp()
return true
case 'l':
mv.keyEnter()
return true
case 'g':
mv.keyHome()
return true
case 'G':
mv.keyEnd()
return true
case 'q':
c := mv.model.GetCurrent()
p := c.parent
if p == nil {
mv.keyBack()
return true
}
return true
}
}
}
@ -81,21 +104,19 @@ func (mv *MenuView) Draw() {
mv.view.SetContent(x, y, ' ', nil, mv.style)
}
}
ex, ey := model.GetBounds()
vx, vy := port.Size()
if ex < vx {
ex = vx
}
_, ey := model.GetBounds()
_, vy := port.Size()
if ey < vy {
ey = vy
}
cy, en, sh := mv.model.GetCursor()
for y := 0; y < ey; y++ {
ch, style := mv.model.GetLine(y)
opt := mv.model.GetOption(y)
style := mv.style
if en && y == cy && sh {
style = mv.selectedStyle
}
puts(port, style, 0, y, string(ch))
puts(port, style, 0, y, opt.String())
}
}
@ -109,7 +130,6 @@ func puts(s views.View, style tcell.Style, x, y int, str string) {
if len(deferred) == 0 {
deferred = append(deferred, ' ')
dwidth = 1
}
deferred = append(deferred, r)
zwj = true
@ -120,7 +140,6 @@ func puts(s views.View, style tcell.Style, x, y int, str string) {
deferred = append(deferred, r)
zwj = false
continue
}
switch runewidth.RuneWidth(r) {
case 0:
@ -133,7 +152,6 @@ func puts(s views.View, style tcell.Style, x, y int, str string) {
if len(deferred) != 0 {
s.SetContent(x+i, y, deferred[0], deferred[1:], style)
i += dwidth
}
deferred = nil
dwidth = 1
@ -145,10 +163,8 @@ func puts(s views.View, style tcell.Style, x, y int, str string) {
}
deferred = nil
dwidth = 2
}
deferred = append(deferred, r)
}
if len(deferred) != 0 {
s.SetContent(x+i, y, deferred[0], deferred[1:], style)
@ -157,12 +173,71 @@ func puts(s views.View, style tcell.Style, x, y int, str string) {
}
}
func (mv *MenuView) keyBack() {
// p := mv.model.GetParent()
// if p == nil {
// return
// }
cur := mv.model.GetCurrent()
if cur.parent == nil {
return
}
mv.model.SetCurrent(*cur.parent)
w, h := mv.model.GetBounds()
mv.port.SetContentSize(w, h, true)
mv.model.SetCursor(cur.y)
mv.port.Center(0, cur.y)
}
func (mv *MenuView) keyEnter() {
var y int
var en bool
if y, en, _ = mv.model.GetCursor(); !en {
return
}
y, _, _ = mv.model.GetCursor()
opt := mv.model.GetOption(y)
opt.y = y
cur := mv.model.GetCurrent()
opt.parent = &cur
switch opt.Action {
case ActionMenu:
if len(opt.Options) == 0 {
return
}
mv.model.SetCurrent(opt)
mv.model.SetCursor(0)
w, h := mv.model.GetBounds()
mv.port.SetContentSize(w, h, true)
case ActionExec:
if len(opt.Args) == 0 {
return
}
cmd := exec.Command(opt.Args[0], opt.Args[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
err := cmd.Run()
mv.Draw()
if err != nil {
return
}
}
}
func (mv *MenuView) keyUp() {
if _, en, _ := mv.model.GetCursor(); !en {
mv.port.ScrollUp(1)
return
}
mv.model.MoveCursor(0, -1)
mv.model.MoveCursor(-1)
mv.MakeCursorVisible()
}
@ -171,7 +246,7 @@ func (mv *MenuView) keyDown() {
mv.port.ScrollDown(1)
return
}
mv.model.MoveCursor(0, 1)
mv.model.MoveCursor(1)
mv.MakeCursorVisible()
}
@ -181,7 +256,7 @@ func (mv *MenuView) keyPgUp() {
mv.port.ScrollUp(vy)
return
}
mv.model.MoveCursor(0, -vy)
mv.model.MoveCursor(-vy)
mv.MakeCursorVisible()
}
@ -191,7 +266,7 @@ func (mv *MenuView) keyPgDn() {
mv.port.ScrollDown(vy)
return
}
mv.model.MoveCursor(0, +vy)
mv.model.MoveCursor(+vy)
mv.MakeCursorVisible()
}

View File

@ -21,6 +21,19 @@ type Repo struct {
Books []Book
}
func (lr *Repo) GetMeta() map[string]map[string][]Book {
meta := map[string]map[string][]Book{}
for _, book := range lr.Books {
for key, value := range book.Meta {
if meta[key] == nil {
meta[key] = map[string][]Book{}
}
meta[key][value] = append(meta[key][value], book)
}
}
return meta
}
func (lr *Repo) Scan() error {
books := lr.scanTree(lr.Path, 1, 3, ".muse")
@ -91,9 +104,9 @@ func readMeta(p string) (map[string]string, error) {
tag := strings.TrimSpace(result[1])
value := strings.TrimSpace(result[2])
meta[tag] = value
} else {
break
}
} else {
break
}
}