Add bindable double-click event (#374)

This commit is contained in:
Junegunn Choi 2015-10-13 02:24:38 +09:00
parent b4ce89bbf5
commit f80ff8c917
3 changed files with 195 additions and 184 deletions

View File

@ -50,6 +50,7 @@ const (
Invalid Invalid
Mouse Mouse
DoubleClick
BTab BTab
BSpace BSpace

View File

@ -343,6 +343,8 @@ func parseKeyChords(str string, message string) map[int]string {
chord = curses.SLeft chord = curses.SLeft
case "shift-right": case "shift-right":
chord = curses.SRight chord = curses.SRight
case "double-click":
chord = curses.DoubleClick
default: default:
if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) { if len(key) == 6 && strings.HasPrefix(lkey, "ctrl-") && isAlphabet(lkey[5]) {
chord = curses.CtrlA + int(lkey[5]) - 'a' chord = curses.CtrlA + int(lkey[5]) - 'a'

View File

@ -180,6 +180,7 @@ func defaultKeymap() map[int]actionType {
keymap[C.Rune] = actRune keymap[C.Rune] = actRune
keymap[C.Mouse] = actMouse keymap[C.Mouse] = actMouse
keymap[C.DoubleClick] = actAccept
return keymap return keymap
} }
@ -858,204 +859,211 @@ func (t *Terminal) Loop() {
action = act action = act
} }
} }
switch action { var doAction func(actionType) bool
case actIgnore: doAction = func(action actionType) bool {
case actExecute: switch action {
if t.cy >= 0 && t.cy < t.merger.Length() { case actIgnore:
item := t.merger.Get(t.cy) case actExecute:
executeCommand(t.execmap[mapkey], item.AsString(t.ansi)) if t.cy >= 0 && t.cy < t.merger.Length() {
} item := t.merger.Get(t.cy)
case actInvalid: executeCommand(t.execmap[mapkey], item.AsString(t.ansi))
t.mutex.Unlock() }
continue case actInvalid:
case actToggleSort: t.mutex.Unlock()
t.sort = !t.sort return false
t.eventBox.Set(EvtSearchNew, t.sort) case actToggleSort:
t.mutex.Unlock() t.sort = !t.sort
continue t.eventBox.Set(EvtSearchNew, t.sort)
case actBeginningOfLine: t.mutex.Unlock()
t.cx = 0 return false
case actBackwardChar: case actBeginningOfLine:
if t.cx > 0 {
t.cx--
}
case actAbort:
req(reqQuit)
case actDeleteChar:
t.delChar()
case actDeleteCharEOF:
if !t.delChar() && t.cx == 0 {
req(reqQuit)
}
case actEndOfLine:
t.cx = len(t.input)
case actCancel:
if len(t.input) == 0 {
req(reqQuit)
} else {
t.yanked = t.input
t.input = []rune{}
t.cx = 0 t.cx = 0
} case actBackwardChar:
case actForwardChar: if t.cx > 0 {
if t.cx < len(t.input) { t.cx--
t.cx++
}
case actBackwardDeleteChar:
if t.cx > 0 {
t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
t.cx--
}
case actSelectAll:
if t.multi {
for i := 0; i < t.merger.Length(); i++ {
item := t.merger.Get(i)
selectItem(item)
} }
req(reqList, reqInfo) case actAbort:
} req(reqQuit)
case actDeselectAll: case actDeleteChar:
if t.multi { t.delChar()
for i := 0; i < t.merger.Length(); i++ { case actDeleteCharEOF:
item := t.merger.Get(i) if !t.delChar() && t.cx == 0 {
delete(t.selected, item.index) req(reqQuit)
} }
req(reqList, reqInfo) case actEndOfLine:
}
case actToggle:
if t.multi && t.merger.Length() > 0 {
toggle()
req(reqList)
}
case actToggleAll:
if t.multi {
for i := 0; i < t.merger.Length(); i++ {
toggleY(i)
}
req(reqList, reqInfo)
}
case actToggleDown:
if t.multi && t.merger.Length() > 0 {
toggle()
t.vmove(-1)
req(reqList)
}
case actToggleUp:
if t.multi && t.merger.Length() > 0 {
toggle()
t.vmove(1)
req(reqList)
}
case actDown:
t.vmove(-1)
req(reqList)
case actUp:
t.vmove(1)
req(reqList)
case actAccept:
req(reqClose)
case actClearScreen:
req(reqRedraw)
case actUnixLineDiscard:
if t.cx > 0 {
t.yanked = copySlice(t.input[:t.cx])
t.input = t.input[t.cx:]
t.cx = 0
}
case actUnixWordRubout:
if t.cx > 0 {
t.rubout("\\s\\S")
}
case actBackwardKillWord:
if t.cx > 0 {
t.rubout("[^[:alnum:]][[:alnum:]]")
}
case actYank:
suffix := copySlice(t.input[t.cx:])
t.input = append(append(t.input[:t.cx], t.yanked...), suffix...)
t.cx += len(t.yanked)
case actPageUp:
t.vmove(t.maxItems() - 1)
req(reqList)
case actPageDown:
t.vmove(-(t.maxItems() - 1))
req(reqList)
case actBackwardWord:
t.cx = findLastMatch("[^[:alnum:]][[:alnum:]]", string(t.input[:t.cx])) + 1
case actForwardWord:
t.cx += findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
case actKillWord:
ncx := t.cx +
findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
if ncx > t.cx {
t.yanked = copySlice(t.input[t.cx:ncx])
t.input = append(t.input[:t.cx], t.input[ncx:]...)
}
case actKillLine:
if t.cx < len(t.input) {
t.yanked = copySlice(t.input[t.cx:])
t.input = t.input[:t.cx]
}
case actRune:
prefix := copySlice(t.input[:t.cx])
t.input = append(append(prefix, event.Char), t.input[t.cx:]...)
t.cx++
case actPreviousHistory:
if t.history != nil {
t.history.override(string(t.input))
t.input = []rune(t.history.previous())
t.cx = len(t.input) t.cx = len(t.input)
} case actCancel:
case actNextHistory: if len(t.input) == 0 {
if t.history != nil { req(reqQuit)
t.history.override(string(t.input)) } else {
t.input = []rune(t.history.next()) t.yanked = t.input
t.cx = len(t.input) t.input = []rune{}
} t.cx = 0
case actMouse: }
me := event.MouseEvent case actForwardChar:
mx, my := me.X, me.Y if t.cx < len(t.input) {
if me.S != 0 { t.cx++
// Scroll }
if t.merger.Length() > 0 { case actBackwardDeleteChar:
if t.multi && me.Mod { if t.cx > 0 {
toggle() t.input = append(t.input[:t.cx-1], t.input[t.cx:]...)
t.cx--
}
case actSelectAll:
if t.multi {
for i := 0; i < t.merger.Length(); i++ {
item := t.merger.Get(i)
selectItem(item)
} }
t.vmove(me.S) req(reqList, reqInfo)
}
case actDeselectAll:
if t.multi {
for i := 0; i < t.merger.Length(); i++ {
item := t.merger.Get(i)
delete(t.selected, item.index)
}
req(reqList, reqInfo)
}
case actToggle:
if t.multi && t.merger.Length() > 0 {
toggle()
req(reqList) req(reqList)
} }
} else if mx >= t.marginInt[3] && mx < C.MaxX()-t.marginInt[1] && case actToggleAll:
my >= t.marginInt[0] && my < C.MaxY()-t.marginInt[2] { if t.multi {
mx -= t.marginInt[3] for i := 0; i < t.merger.Length(); i++ {
my -= t.marginInt[0] toggleY(i)
mx = util.Constrain(mx-len(t.prompt), 0, len(t.input))
if !t.reverse {
my = t.maxHeight() - my - 1
}
min := 2 + len(t.header)
if t.inlineInfo {
min--
}
if me.Double {
// Double-click
if my >= min {
if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
req(reqClose)
}
} }
} else if me.Down { req(reqList, reqInfo)
if my == 0 && mx >= 0 { }
// Prompt case actToggleDown:
t.cx = mx if t.multi && t.merger.Length() > 0 {
} else if my >= min { toggle()
// List t.vmove(-1)
if t.vset(t.offset+my-min) && t.multi && me.Mod { req(reqList)
}
case actToggleUp:
if t.multi && t.merger.Length() > 0 {
toggle()
t.vmove(1)
req(reqList)
}
case actDown:
t.vmove(-1)
req(reqList)
case actUp:
t.vmove(1)
req(reqList)
case actAccept:
req(reqClose)
case actClearScreen:
req(reqRedraw)
case actUnixLineDiscard:
if t.cx > 0 {
t.yanked = copySlice(t.input[:t.cx])
t.input = t.input[t.cx:]
t.cx = 0
}
case actUnixWordRubout:
if t.cx > 0 {
t.rubout("\\s\\S")
}
case actBackwardKillWord:
if t.cx > 0 {
t.rubout("[^[:alnum:]][[:alnum:]]")
}
case actYank:
suffix := copySlice(t.input[t.cx:])
t.input = append(append(t.input[:t.cx], t.yanked...), suffix...)
t.cx += len(t.yanked)
case actPageUp:
t.vmove(t.maxItems() - 1)
req(reqList)
case actPageDown:
t.vmove(-(t.maxItems() - 1))
req(reqList)
case actBackwardWord:
t.cx = findLastMatch("[^[:alnum:]][[:alnum:]]", string(t.input[:t.cx])) + 1
case actForwardWord:
t.cx += findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
case actKillWord:
ncx := t.cx +
findFirstMatch("[[:alnum:]][^[:alnum:]]|(.$)", string(t.input[t.cx:])) + 1
if ncx > t.cx {
t.yanked = copySlice(t.input[t.cx:ncx])
t.input = append(t.input[:t.cx], t.input[ncx:]...)
}
case actKillLine:
if t.cx < len(t.input) {
t.yanked = copySlice(t.input[t.cx:])
t.input = t.input[:t.cx]
}
case actRune:
prefix := copySlice(t.input[:t.cx])
t.input = append(append(prefix, event.Char), t.input[t.cx:]...)
t.cx++
case actPreviousHistory:
if t.history != nil {
t.history.override(string(t.input))
t.input = []rune(t.history.previous())
t.cx = len(t.input)
}
case actNextHistory:
if t.history != nil {
t.history.override(string(t.input))
t.input = []rune(t.history.next())
t.cx = len(t.input)
}
case actMouse:
me := event.MouseEvent
mx, my := me.X, me.Y
if me.S != 0 {
// Scroll
if t.merger.Length() > 0 {
if t.multi && me.Mod {
toggle() toggle()
} }
t.vmove(me.S)
req(reqList) req(reqList)
} }
} else if mx >= t.marginInt[3] && mx < C.MaxX()-t.marginInt[1] &&
my >= t.marginInt[0] && my < C.MaxY()-t.marginInt[2] {
mx -= t.marginInt[3]
my -= t.marginInt[0]
mx = util.Constrain(mx-len(t.prompt), 0, len(t.input))
if !t.reverse {
my = t.maxHeight() - my - 1
}
min := 2 + len(t.header)
if t.inlineInfo {
min--
}
if me.Double {
// Double-click
if my >= min {
if t.vset(t.offset+my-min) && t.cy < t.merger.Length() {
return doAction(t.keymap[C.DoubleClick])
}
}
} else if me.Down {
if my == 0 && mx >= 0 {
// Prompt
t.cx = mx
} else if my >= min {
// List
if t.vset(t.offset+my-min) && t.multi && me.Mod {
toggle()
}
req(reqList)
}
}
} }
} }
return true
}
if !doAction(action) {
continue
} }
changed := string(previousInput) != string(t.input) changed := string(previousInput) != string(t.input)
t.mutex.Unlock() // Must be unlocked before touching reqBox t.mutex.Unlock() // Must be unlocked before touching reqBox