From 19bddebfaa6c7370e886b86154116b381f950338 Mon Sep 17 00:00:00 2001 From: SinkevichMM Date: Wed, 9 Sep 2020 15:12:21 +0700 Subject: [PATCH 1/2] make some helpers for views navigate --- _examples/buffer_navigate.go | 234 +++++++++++++++++++++++++++++++++++ view.go | 34 ++++- 2 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 _examples/buffer_navigate.go diff --git a/_examples/buffer_navigate.go b/_examples/buffer_navigate.go new file mode 100644 index 00000000..7e844dba --- /dev/null +++ b/_examples/buffer_navigate.go @@ -0,0 +1,234 @@ +// Copyright 2014 The gocui Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "log" + "strings" + "time" + + "github.com/awesome-gocui/gocui" +) + +type randomValue struct { + id int + val string + prop1 string + prop2 string + prop3 string +} + +var rndVals = []randomValue{ + {id: 0, val: "first", prop1: "prop first", prop2: "second property", prop3: "next very important data"}, + {id: 1, val: "next", prop1: "test1", prop2: "test2", prop3: "test3"}, + {id: 2, val: "another", prop1: "qqq", prop2: "www", prop3: "eee"}, + {id: 3, val: "very long value", prop1: "very", prop2: "long", prop3: "value"}, + {id: 4, val: "last", prop1: "the", prop2: "last", prop3: "value"}, + {id: 5, val: "first", prop1: "prop first", prop2: "second property", prop3: "next very important data"}, + {id: 6, val: "next", prop1: "test1", prop2: "test2", prop3: "test3"}, + {id: 7, val: "another", prop1: "qqq", prop2: "www", prop3: "eee"}, + {id: 8, val: "very long value", prop1: "very", prop2: "long", prop3: "value"}, + {id: 9, val: "last", prop1: "the", prop2: "last", prop3: "value"}, + {id: 10, val: "first", prop1: "prop first", prop2: "second property", prop3: "next very important data"}, +} + +func setCurrentViewOnTop(g *gocui.Gui, name string) (*gocui.View, error) { + if _, err := g.SetCurrentView(name); err != nil { + return nil, err + } + + return g.SetViewOnTop(name) +} + +func layout(g *gocui.Gui) error { + var ( + v1 *gocui.View + err error + ) + + if v1, err = g.SetView("v1", 0, 0, 20, 12, 0); err != nil { + if !gocui.IsUnknownView(err) { + return err + } + v1.Highlight = true + v1.Title = "val" + v1.Wrap = true + + if _, err = setCurrentViewOnTop(g, "v1"); err != nil { + return err + } + str := "" + for k, r := range rndVals { + str += fmt.Sprintf("[%d] ID:%d %s\n", k, r.id, r.val) + } + + fmt.Fprint(v1, strings.TrimSpace(str)) + } + + if v, err := g.SetView("v2", 21, 0, 60, 5, 0); err != nil { + if !gocui.IsUnknownView(err) { + return err + } + v.Title = "props" + v.Wrap = true + + fmt.Fprint(v, BuffViewV2(&rndVals[0])) + } + + if v, err := g.SetView("v3", 21, 6, 60, 12, 0); err != nil { + if !gocui.IsUnknownView(err) { + return err + } + v.Wrap = true + + fmt.Fprint(v, BuffViewV3(v1)) + } + + return err +} + +func quit(g *gocui.Gui, v *gocui.View) error { + return gocui.ErrQuit +} + +func cursorDownV1(g *gocui.Gui, v *gocui.View) error { + v.SetViewLineDown() + + navigateV1(g) + + return nil +} + +func cursorUpV1(g *gocui.Gui, v *gocui.View) error { + v.SetViewLineUp() + + navigateV1(g) + + return nil +} + +func mouseSelectV1(g *gocui.Gui, v *gocui.View) error { + err := mouseSelect(g, v) + + if err != nil { + return err + } + navigateV1(g) + + return err +} + +func mouseSelect(g *gocui.Gui, v *gocui.View) (err error) { + if _, err := g.SetCurrentView(v.Name()); err != nil { + return err + } + + return err +} + +func navigateV1(g *gocui.Gui) { + v, _ := g.View("v1") + pos := v.BufferLinePosition() + if pos >= 0 { + fillChildView("v2", g, BuffViewV2(&rndVals[pos])) + } else { + fillChildView("v2", g, "") + } + + fillChildView("v3", g, BuffViewV3(v)) +} + +func fillChildView(viewName string, g *gocui.Gui, data string) { + vv, err := g.View(viewName) + + if err != nil { + return + } + + if vv != nil { + vv.Clear() + + if data == "" { + return + } + fmt.Fprint(vv, data) + } +} + +func BuffViewV2(itm *randomValue) (str string) { + str += fmt.Sprintf("ID: %d\n", itm.id) + str += fmt.Sprintf("prop1: %s\n", itm.prop1) + str += fmt.Sprintf("prop2: %s\n", itm.prop2) + str += fmt.Sprintf("prop3: %s", itm.prop3) + + return str +} + +func BuffViewV3(v *gocui.View) (str string) { + str += fmt.Sprintf("buf line index: %d\n", v.BufferLinePosition()) + str += fmt.Sprintf("buf lines count: %d\n", len(v.BufferLines())) + str += fmt.Sprintf("view lines count: %d\n", len(v.ViewBufferLines())) + _, y := v.Cursor() + str += fmt.Sprintf("cursor y: %d\n", y) + _, y = v.Origin() + str += fmt.Sprintf("origin y: %d", y) + + return str +} + +func serviceFunc(g *gocui.Gui) { + time.Sleep(50 * time.Millisecond) + + g.Update(func(g *gocui.Gui) error { + v, err := g.View("v1") + if err != nil { + return nil + } + + fillChildView("v3", g, BuffViewV3(v)) + + return nil + }) +} + +func main() { + g, err := gocui.NewGui(gocui.OutputNormal, true) + if err != nil { + log.Panicln(err) + } + defer g.Close() + + g.Highlight = true + g.Cursor = false + g.Mouse = true + //g.SelBgColor = gocui.ColorGreen + g.SelFgColor = gocui.ColorGreen + g.SelFrameColor = gocui.ColorGreen + + g.SetManagerFunc(layout) + + if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("v1", gocui.KeyArrowDown, gocui.ModNone, cursorDownV1); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("v1", gocui.KeyArrowUp, gocui.ModNone, cursorUpV1); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("v1", gocui.MouseLeft, gocui.ModNone, mouseSelectV1); err != nil { + log.Panicln(err) + } + + go serviceFunc(g) + + if err := g.MainLoop(); err != nil && !gocui.IsQuit(err) { + log.Panicln(err) + } +} diff --git a/view.go b/view.go index 6d547275..9b849753 100644 --- a/view.go +++ b/view.go @@ -213,6 +213,17 @@ func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error { return nil } +// BufferLinePosition returns the index current row in the view's internal +// buffer. If out of bounds of the buffer return -1 +func (v *View) BufferLinePosition() (y int) { + _, y, _ = v.realPosition(0, v.cy) + // out of buffer + if y > len(v.BufferLines())-1 { + y = -1 + } + return y +} + // SetCursor sets the cursor position of the view at the given point, // relative to the view. It checks if the position is valid. func (v *View) SetCursor(x, y int) error { @@ -225,6 +236,27 @@ func (v *View) SetCursor(x, y int) error { return nil } +// SetViewLineUp sets the cursor position of the view at the one line up +func (v *View) SetViewLineUp() { + if v.cy > 0 { + v.cy -= 1 + } else if v.oy > 0 { + v.oy -= 1 + } +} + +// SetViewLineDown sets the cursor position of the view at the one line down +func (v *View) SetViewLineDown() { + _, maxY := v.Size() + if v.cy+v.oy < v.ViewLinesHeight()-1 { + if v.cy >= maxY-1 { + v.oy += 1 + } else { + v.cy += 1 + } + } +} + // Cursor returns the cursor position of the view. func (v *View) Cursor() (x, y int) { return v.cx, v.cy @@ -707,7 +739,7 @@ func (v *View) Word(x, y int) (string, error) { } else { nr = nr + x } - return string(str[nl:nr]), nil + return str[nl:nr], nil } // indexFunc allows to split lines by words taking into account spaces From 0009d0abe7df0cc1121a89785b94263c1aab5dc0 Mon Sep 17 00:00:00 2001 From: Mikhail Sinkevich Date: Thu, 17 Sep 2020 23:01:55 +0700 Subject: [PATCH 2/2] fixed delete of rune fixed delete of rune when navigating with the mouse --- edit.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/edit.go b/edit.go index b5630df3..ab41c635 100644 --- a/edit.go +++ b/edit.go @@ -370,6 +370,17 @@ func (v *View) writeRune(x, y int, ch rune) error { // position corresponding to the point (x, y). // returns the amount of columns that where removed. func (v *View) deleteRune(x, y int) (int, error) { + + bf := v.BufferLines() + pos := v.BufferLinePosition() + + if pos > 0 { + err := v.SetLine(pos, bf[pos]) + if err != nil { + return 0, err + } + } + v.tainted = true x, y, err := v.realPosition(x, y) @@ -381,18 +392,9 @@ func (v *View) deleteRune(x, y int) (int, error) { return 0, errors.New("invalid point") } - var tw int - for i := range v.lines[y] { - w := runewidth.RuneWidth(v.lines[y][i].chr) - tw += w - if tw > x { - v.lines[y] = append(v.lines[y][:i], v.lines[y][i+1:]...) - return w, nil - } - - } + v.lines[y] = append(v.lines[y][:x], v.lines[y][x+1:]...) - return 0, nil + return 1, nil } // mergeLines merges the lines "y" and "y+1" if possible.