mirror of
https://github.com/jroimartin/gocui.git
synced 2025-04-24 13:48:51 +08:00
Initial implementation of the full edition mode
This commit is contained in:
parent
b1ad4a9fa7
commit
622e7cbdf9
@ -46,32 +46,6 @@ func cursorUp(g *gocui.Gui, v *gocui.View) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func cursorLeft(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
ox, oy := v.Origin()
|
||||
cx, cy := v.Cursor()
|
||||
if err := v.SetCursor(cx-1, cy); err != nil && ox > 0 {
|
||||
if err := v.SetOrigin(ox-1, oy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cursorRight(g *gocui.Gui, v *gocui.View) error {
|
||||
if v != nil {
|
||||
cx, cy := v.Cursor()
|
||||
if err := v.SetCursor(cx+1, cy); err != nil {
|
||||
ox, oy := v.Origin()
|
||||
if err := v.SetOrigin(ox+1, oy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLine(g *gocui.Gui, v *gocui.View) error {
|
||||
var l string
|
||||
var err error
|
||||
@ -115,16 +89,10 @@ func keybindings(g *gocui.Gui) error {
|
||||
if err := g.SetKeybinding("main", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
|
||||
if err := g.SetKeybinding("side", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone, cursorLeft); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, cursorRight); err != nil {
|
||||
if err := g.SetKeybinding("side", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
||||
|
138
edit.go
138
edit.go
@ -6,65 +6,137 @@ package gocui
|
||||
|
||||
import "github.com/nsf/termbox-go"
|
||||
|
||||
// handleEdit manages the edition mode.
|
||||
func (g *Gui) handleEdit(v *View, ev *termbox.Event) error {
|
||||
// handleEdit manages the edition mode. We do not handle errors here because if
|
||||
// an error happens, it is enough to keep the view without modifications.
|
||||
func (g *Gui) handleEdit(v *View, ev *termbox.Event) {
|
||||
switch {
|
||||
case ev.Ch != 0 && ev.Mod == 0:
|
||||
return v.editWrite(ev.Ch)
|
||||
v.editWrite(ev.Ch)
|
||||
case ev.Key == termbox.KeySpace:
|
||||
return v.editWrite(' ')
|
||||
v.editWrite(' ')
|
||||
case ev.Key == termbox.KeyBackspace || ev.Key == termbox.KeyBackspace2:
|
||||
return v.editDelete(true)
|
||||
v.editDelete(true)
|
||||
case ev.Key == termbox.KeyDelete:
|
||||
return v.editDelete(false)
|
||||
v.editDelete(false)
|
||||
case ev.Key == termbox.KeyInsert:
|
||||
v.overwrite = !v.overwrite
|
||||
case ev.Key == termbox.KeyEnter:
|
||||
return v.editLine()
|
||||
v.editNewLine()
|
||||
case ev.Key == termbox.KeyArrowDown:
|
||||
v.moveCursor(0, 1, false)
|
||||
case ev.Key == termbox.KeyArrowUp:
|
||||
v.moveCursor(0, -1, false)
|
||||
case ev.Key == termbox.KeyArrowLeft:
|
||||
v.moveCursor(-1, 0, false)
|
||||
case ev.Key == termbox.KeyArrowRight:
|
||||
v.moveCursor(1, 0, false)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// editWrite writes a rune at the cursor position.
|
||||
func (v *View) editWrite(ch rune) error {
|
||||
func (v *View) editWrite(ch rune) {
|
||||
v.writeRune(v.cx, v.cy, ch)
|
||||
if err := v.SetCursor(v.cx+1, v.cy); err != nil {
|
||||
if err := v.SetOrigin(v.ox+1, v.oy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
v.moveCursor(1, 0, true)
|
||||
}
|
||||
|
||||
// editDelete deletes a rune at the cursor position. back determines
|
||||
// the direction.
|
||||
func (v *View) editDelete(back bool) error {
|
||||
func (v *View) editDelete(back bool) {
|
||||
if back {
|
||||
v.deleteRune(v.cx-1, v.cy)
|
||||
if err := v.SetCursor(v.cx-1, v.cy); err != nil && v.ox > 0 {
|
||||
if err := v.SetOrigin(v.ox-1, v.oy); err != nil {
|
||||
return err
|
||||
}
|
||||
if v.cx == 0 {
|
||||
v.mergeLines(v.cy - 1)
|
||||
} else {
|
||||
v.deleteRune(v.cx-1, v.cy)
|
||||
}
|
||||
v.moveCursor(-1, 0, true)
|
||||
} else {
|
||||
v.deleteRune(v.cx, v.cy)
|
||||
y := v.oy + v.cy
|
||||
if y >= 0 && y < len(v.viewLines) && v.cx == len(v.viewLines[y].line) {
|
||||
v.mergeLines(v.cy)
|
||||
} else {
|
||||
v.deleteRune(v.cx, v.cy)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// editLine inserts a new line under the cursor.
|
||||
func (v *View) editLine() error {
|
||||
// editNewLine inserts a new line under the cursor.
|
||||
func (v *View) editNewLine() {
|
||||
v.breakLine(v.cx, v.cy)
|
||||
if err := v.SetCursor(v.cx, v.cy+1); err != nil {
|
||||
if err := v.SetOrigin(v.ox, v.oy+1); err != nil {
|
||||
return err
|
||||
|
||||
y := v.oy + v.cy
|
||||
if y >= len(v.viewLines) || (y >= 0 && y < len(v.viewLines) &&
|
||||
!(v.Wrap && v.cx == 0 && v.viewLines[y].linesX > 0)) {
|
||||
// new line at the end of the buffer or
|
||||
// cursor is not at the beginning of a wrapped line
|
||||
v.ox = 0
|
||||
v.cx = 0
|
||||
v.moveCursor(0, 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
// moveCursor moves the cursor taking into account the line or view widths and
|
||||
// moves the origin when necessary. If writeMode is false, the cursor jumps to
|
||||
// the next line when it reaches the end of the line, otherwise it jumps when
|
||||
// the cursor reaches the width of the view.
|
||||
func (v *View) moveCursor(dx, dy int, writeMode bool) {
|
||||
maxX, maxY := v.Size()
|
||||
cx, cy := v.cx+dx, v.cy+dy
|
||||
y := v.oy + cy
|
||||
|
||||
var curLineWidth, prevLineWidth int
|
||||
// get the width of the current line
|
||||
if writeMode {
|
||||
curLineWidth = maxX - 1
|
||||
} else {
|
||||
if y >= 0 && y < len(v.viewLines) {
|
||||
w := len(v.viewLines[y].line)
|
||||
if w < maxX {
|
||||
curLineWidth = w
|
||||
} else {
|
||||
curLineWidth = maxX - 1
|
||||
}
|
||||
} else {
|
||||
curLineWidth = 0
|
||||
}
|
||||
}
|
||||
if err := v.SetCursor(0, v.cy); err != nil {
|
||||
return err
|
||||
// get the width of the previous line
|
||||
if y-1 >= 0 && y-1 < len(v.viewLines) {
|
||||
prevLineWidth = len(v.viewLines[y-1].line)
|
||||
} else {
|
||||
prevLineWidth = 0
|
||||
}
|
||||
if err := v.SetOrigin(0, v.oy); err != nil {
|
||||
return err
|
||||
|
||||
// adjust cursor's x position and view's x origin
|
||||
if cx > curLineWidth { // move to next line
|
||||
if dx > 0 { // horizontal movement
|
||||
v.cx = 0
|
||||
cy += 1
|
||||
} else { // vertical movement
|
||||
if curLineWidth > 0 {
|
||||
v.cx = curLineWidth
|
||||
} else {
|
||||
v.cx = 0
|
||||
}
|
||||
}
|
||||
} else if cx < 0 { // move to previous line
|
||||
if prevLineWidth > 0 {
|
||||
v.cx = prevLineWidth
|
||||
} else {
|
||||
v.cx = 0
|
||||
}
|
||||
cy -= 1
|
||||
} else { // stay on the same line
|
||||
v.cx = cx
|
||||
}
|
||||
|
||||
// adjust cursor's y position and view's y origin
|
||||
if cy >= maxY {
|
||||
v.oy += 1
|
||||
} else if cy < 0 {
|
||||
if v.oy > 0 {
|
||||
v.oy -= 1
|
||||
}
|
||||
} else {
|
||||
v.cy = cy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
5
gui.go
5
gui.go
@ -455,15 +455,12 @@ func horizontalRune(ch rune) bool {
|
||||
// currentView's internal buffer is modified if currentView.Editable is true.
|
||||
func (g *Gui) onKey(ev *termbox.Event) error {
|
||||
if g.currentView != nil && g.currentView.Editable {
|
||||
if err := g.handleEdit(g.currentView, ev); err != nil {
|
||||
return err
|
||||
}
|
||||
g.handleEdit(g.currentView, ev)
|
||||
}
|
||||
|
||||
var cv string
|
||||
if g.currentView != nil {
|
||||
cv = g.currentView.name
|
||||
|
||||
}
|
||||
for _, kb := range g.keybindings {
|
||||
if kb.h == nil {
|
||||
|
53
view.go
53
view.go
@ -328,27 +328,18 @@ func (v *View) writeRune(x, y int, ch rune) error {
|
||||
}
|
||||
|
||||
if y >= len(v.lines) {
|
||||
if y >= cap(v.lines) {
|
||||
s := make([][]rune, y+1, (y+1)*2)
|
||||
copy(s, v.lines)
|
||||
v.lines = s
|
||||
} else {
|
||||
v.lines = v.lines[:y+1]
|
||||
}
|
||||
s := make([][]rune, y-len(v.lines)+1)
|
||||
v.lines = append(v.lines, s...)
|
||||
}
|
||||
if v.lines[y] == nil {
|
||||
v.lines[y] = make([]rune, x+1, (x+1)*2)
|
||||
} else if x >= len(v.lines[y]) {
|
||||
if x >= cap(v.lines[y]) {
|
||||
s := make([]rune, x+1, (x+1)*2)
|
||||
copy(s, v.lines[y])
|
||||
v.lines[y] = s
|
||||
} else {
|
||||
v.lines[y] = v.lines[y][:x+1]
|
||||
}
|
||||
|
||||
olen := len(v.lines[y])
|
||||
if x >= len(v.lines[y]) {
|
||||
s := make([]rune, x-len(v.lines[y])+1)
|
||||
v.lines[y] = append(v.lines[y], s...)
|
||||
}
|
||||
if !v.overwrite {
|
||||
v.lines[y] = append(v.lines[y], ' ')
|
||||
|
||||
if !v.overwrite && x < olen {
|
||||
v.lines[y] = append(v.lines[y], '\x00')
|
||||
copy(v.lines[y][x+1:], v.lines[y][x:])
|
||||
}
|
||||
v.lines[y][x] = ch
|
||||
@ -365,13 +356,33 @@ func (v *View) deleteRune(x, y int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if x < 0 || y < 0 || y >= len(v.lines) || v.lines[y] == nil || x >= len(v.lines[y]) {
|
||||
if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
|
||||
return errors.New("invalid point")
|
||||
}
|
||||
v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// mergeLines merges the lines "y" and "y+1" if possible.
|
||||
func (v *View) mergeLines(y int) error {
|
||||
v.tainted = true
|
||||
|
||||
_, y, err := v.realPosition(0, y)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if y < 0 || y >= len(v.lines) {
|
||||
return errors.New("invalid point")
|
||||
}
|
||||
|
||||
if y < len(v.lines)-1 { // otherwise we don't need to merge anything
|
||||
v.lines[y] = append(v.lines[y], v.lines[y+1]...)
|
||||
v.lines = append(v.lines[:y+1], v.lines[y+2:]...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// breakLine breaks a line of the internal buffer at the position corresponding
|
||||
// to the point (x, y).
|
||||
func (v *View) breakLine(x, y int) error {
|
||||
@ -437,7 +448,7 @@ func (v *View) Word(x, y int) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
|
||||
if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
|
||||
return "", errors.New("invalid point")
|
||||
}
|
||||
l := string(v.lines[y])
|
||||
|
Loading…
x
Reference in New Issue
Block a user