package mode
import (
)
type Navigation interface {
tk.Widget
SelectedName() string
Select(f func(tk.ListBoxState) int)
ScrollPreview(delta int)
Ascend()
Descend()
MutateFiltering(f func(bool) bool)
MutateShowHidden(f func(bool) bool)
}
type NavigationSpec struct {
Bindings tk.Bindings
Cursor NavigationCursor
WidthRatio func() [3]int
Filter FilterSpec
}
type navigationState struct {
Filtering bool
ShowHidden bool
}
type navigation struct {
NavigationSpec
app cli.App
codeArea tk.CodeArea
colView tk.ColView
lastFilter string
stateMutex sync.RWMutex
state navigationState
}
func ( *navigation) ( func(*navigationState)) {
.stateMutex.Lock()
defer .stateMutex.Unlock()
(&.state)
}
func ( *navigation) () navigationState {
.stateMutex.RLock()
defer .stateMutex.RUnlock()
return .state
}
func ( *navigation) ( term.Event) bool {
if .colView.Handle() {
return true
}
if .CopyState().Filtering {
if .codeArea.Handle() {
:= .codeArea.CopyState().Buffer.Content
if != .lastFilter {
.lastFilter =
updateState(, "")
}
return true
}
return false
}
return .app.CodeArea().Handle()
}
func ( *navigation) (, int) *term.Buffer {
:= .codeArea.Render(, )
:= .colView.Render(, -len(.Lines))
.Extend(, false)
return
}
func ( *navigation) () bool {
return .CopyState().Filtering
}
func ( *navigation) () {
:= ""
, := .Cursor.Current()
if == nil {
= .Name()
}
= .Cursor.Ascend()
if != nil {
.app.Notify(.Error())
} else {
.codeArea.MutateState(func( *tk.CodeAreaState) {
.Buffer = tk.CodeBuffer{}
})
updateState(, )
}
}
func ( *navigation) () {
, := .colView.CopyState().Columns[1].(tk.ListBox)
if ! {
return
}
:= .CopyState()
if .Items.Len() == 0 {
return
}
:= .Items.(fileItems)[.Selected]
if !.IsDirDeep() {
return
}
:= .Cursor.Descend(.Name())
if != nil {
.app.Notify(.Error())
} else {
.codeArea.MutateState(func( *tk.CodeAreaState) {
.Buffer = tk.CodeBuffer{}
})
updateState(, "")
}
}
func ( cli.App, NavigationSpec) Navigation {
if .Cursor == nil {
.Cursor = NewOSNavigationCursor()
}
if .WidthRatio == nil {
.WidthRatio = func() [3]int { return [3]int{1, 3, 4} }
}
var *navigation
= &navigation{
NavigationSpec: ,
app: ,
codeArea: tk.NewCodeArea(tk.CodeAreaSpec{
Prompt: func() ui.Text {
if .CopyState().ShowHidden {
return modeLine(" NAVIGATING (show hidden) ", true)
}
return modeLine(" NAVIGATING ", true)
},
Highlighter: .Filter.Highlighter,
}),
colView: tk.NewColView(tk.ColViewSpec{
Bindings: .Bindings,
Weights: func(int) []int {
:= .WidthRatio()
return [:]
},
OnLeft: func(tk.ColView) { .ascend() },
OnRight: func(tk.ColView) { .descend() },
}),
}
updateState(, "")
return
}
func ( *navigation) () string {
, := .colView.CopyState().Columns[1].(tk.ListBox)
if ! {
return ""
}
:= .CopyState()
if 0 <= .Selected && .Selected < .Items.Len() {
return .Items.(fileItems)[.Selected].Name()
}
return ""
}
func ( *navigation, string) {
:= .colView
:= .Cursor
:= .lastFilter
:= .CopyState().ShowHidden
var , tk.Widget
.MutateState(func( *tk.ColViewState) {
* = tk.ColViewState{
Columns: []tk.Widget{
tk.Empty{}, tk.Empty{}, tk.Empty{}},
FocusColumn: 1,
}
})
, := .Parent()
if == nil {
= makeCol(, )
} else {
= makeErrCol()
}
, := .Current()
if == nil {
= makeColInner(
,
.Filter.makePredicate(),
,
func( tk.Items, int) {
:= makeCol(.(fileItems)[], )
.MutateState(func( *tk.ColViewState) {
.Columns[2] =
})
})
tryToSelectName(, .Name())
if != "" {
tryToSelectName(, )
}
} else {
= makeErrCol()
tryToSelectNothing()
}
.MutateState(func( *tk.ColViewState) {
.Columns[0] =
.Columns[1] =
})
}
func ( tk.Widget) {
, := .(tk.ListBox)
if ! {
return
}
.Select(func(tk.ListBoxState) int { return -1 })
}
func ( tk.Widget, string) {
, := .(tk.ListBox)
if ! {
return
}
.Select(func( tk.ListBoxState) int {
, := .Items.(fileItems)
if ! {
return 0
}
for , := range {
if .Name() == {
return
}
}
return 0
})
}
func ( NavigationFile, bool) tk.Widget {
return makeColInner(, func(string) bool { return true }, , nil)
}
func ( NavigationFile, func(string) bool, bool, func(tk.Items, int)) tk.Widget {
, , := .Read()
if != nil {
return makeErrCol()
}
if != nil {
var []NavigationFile
for , := range {
:= .Name()
:= len() > 0 && [0] == '.'
if () && ( || !) {
= append(, )
}
}
=
sort.Slice(, func(, int) bool {
return [].Name() < [].Name()
})
return tk.NewListBox(tk.ListBoxSpec{
Padding: 1, ExtendStyle: true, OnSelect: ,
State: tk.ListBoxState{Items: fileItems()},
})
}
:= strings.Split(sanitize(string()), "\n")
return tk.NewTextView(tk.TextViewSpec{
State: tk.TextViewState{Lines: },
Scrollable: true,
})
}
func ( error) tk.Widget {
return tk.Label{Content: ui.T(.Error(), ui.FgRed)}
}
type fileItems []NavigationFile
func ( fileItems) ( int) ui.Text {
return [].ShowName()
}
func ( fileItems) () int { return len() }
func ( string) string {
var strings.Builder
for , := range {
if == '\t' {
.WriteString(" ")
} else if == '\n' || unicode.IsGraphic() {
.WriteRune()
}
}
return .String()
}
func ( *navigation) ( func(tk.ListBoxState) int) {
if , := .colView.CopyState().Columns[1].(tk.ListBox); {
.Select()
}
}
func ( *navigation) ( int) {
if , := .colView.CopyState().Columns[2].(tk.TextView); {
.ScrollBy()
}
}
func ( *navigation) () {
.colView.Left()
}
func ( *navigation) () {
.colView.Right()
}
func ( *navigation) ( func(bool) bool) {
.MutateState(func( *navigationState) { .Filtering = (.Filtering) })
}
func ( *navigation) ( func(bool) bool) {
.MutateState(func( *navigationState) { .ShowHidden = (.ShowHidden) })
updateState(, .SelectedName())
}