Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make some helpers for views navigate #70

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 234 additions & 0 deletions _examples/buffer_navigate.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
24 changes: 13 additions & 11 deletions edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:]...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested this with half width characters?
For example:

半角


return 0, nil
return 1, nil
}

// mergeLines merges the lines "y" and "y+1" if possible.
Expand Down
34 changes: 33 additions & 1 deletion view.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should'd we add an extra v.Editable check here?

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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change this code to be like this, so we don't have nested ifs:

Suggested change
if v.cy+v.oy < v.ViewLinesHeight()-1 {
if v.cy+v.oy >= v.ViewLinesHeight()-1 {
return
}

if v.cy >= maxY-1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should'd we add an extra v.Editable check here?

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
Expand Down Expand Up @@ -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
Expand Down