From fea1a1adbb03d657724be9b9994ba6b11dfe625f Mon Sep 17 00:00:00 2001 From: Petter Rodhelind Date: Fri, 16 Mar 2018 12:33:26 +0100 Subject: Redesign. --- ui/cli.go | 2 +- ui/tcell/layout.go | 32 ++++------------- ui/tcell/style.go | 23 +++++++----- ui/tcell/tcell.go | 101 ++++++++++++++++++++++++++++++----------------------- ui/tcell/view.go | 72 ++++++++++++++++++++++++-------------- ui/tcell/window.go | 46 ++++++++++++++++-------- ui/ui.go | 7 ---- 7 files changed, 155 insertions(+), 128 deletions(-) (limited to 'ui') diff --git a/ui/cli.go b/ui/cli.go index a7cf9e4..a28bd03 100644 --- a/ui/cli.go +++ b/ui/cli.go @@ -47,7 +47,7 @@ outer: if len(input) < 2 { break } - c.ed.Current().Write([]byte(strings.Join(input[1:], " "))) + //c.ed.Current().Write([]byte(strings.Join(input[1:], " "))) default: fmt.Println("?") } diff --git a/ui/tcell/layout.go b/ui/tcell/layout.go index 599fb6a..bc30f8c 100644 --- a/ui/tcell/layout.go +++ b/ui/tcell/layout.go @@ -80,7 +80,8 @@ func (wrk *Workspace) Draw() { // draw vertical lines between cols for x, y := col.x+col.w+1, wrk.y; y < wrk.y+wrk.h; y++ { - screen.SetContent(x, y, '|', nil, vertlineStyle) + // 2502 + screen.SetContent(x, y, '\u007c', nil, vertlineStyle) } } } @@ -101,11 +102,8 @@ func (c *Column) CloseWindow(w *Window) { } c.windows = c.windows[:j] - // If we deleted the current window (probably), select another if CurWin == w { - all := AllWindows() - - // If we are out of windows in our own column, pick another or exit + // If we are not out of windows in our own column, pick another or exit if len(c.windows) > 0 { CurWin = c.windows[j-1] } else { @@ -115,18 +113,7 @@ func (c *Column) CloseWindow(w *Window) { // clear clutter screen.Clear() - // find another window to focus or exit - if len(all) > 0 { - CurWin = AllWindows()[0] - - } else { - ed.Run("Exit") - } - } - - // if the only win left is the message win, close all - if len(all) == 1 && CurWin.Name() == FnMessageWin { - ed.Run("Exit") + return } } @@ -141,12 +128,7 @@ func (c *Column) Resize(x, y, w, h int) { } func (c *Column) ResizeWindows() { - var n int - for _, win := range c.windows { - if !win.hidden { - n++ - } - } + n := len(c.windows) var remainder int if n > 0 { @@ -163,8 +145,6 @@ func (c *Column) ResizeWindows() { func (c *Column) Draw() { for _, win := range c.windows { - if !win.hidden { - win.Draw() - } + win.Draw() } } diff --git a/ui/tcell/style.go b/ui/tcell/style.go index 0dcb565..a179c1f 100644 --- a/ui/tcell/style.go +++ b/ui/tcell/style.go @@ -24,19 +24,24 @@ var ( // initStyles initializes the different styles (colors for background/foreground). func initStyles() error { - bodyStyle = tcell.StyleDefault. - Background(tcell.NewHexColor(0xffffea)). - Foreground(tcell.ColorBlack) + bodyStyle = tcell.StyleDefault + + // bodyStyle = tcell.StyleDefault. + // Background(tcell.NewHexColor(0xffffea)). + // Foreground(tcell.ColorBlack) + bodyCursorStyle = bodyStyle. Background(tcell.NewHexColor(0xeaea9e)) - bodyHilightStyle = bodyStyle. - Background(tcell.NewHexColor(0xa6a65a)) + + bodyHilightStyle = bodyStyle.Reverse(true) + // bodyHilightStyle = bodyStyle. + // Background(tcell.NewHexColor(0xa6a65a)) unprintableStyle = bodyStyle. Foreground(tcell.ColorRed) - tagStyle = tcell.StyleDefault. - Background(tcell.NewHexColor(0xeaffff)). - Foreground(tcell.ColorBlack) + tagStyle = tcell.StyleDefault.Reverse(true) + //Background(tcell.NewHexColor(0xeaffff)). + //Foreground(tcell.ColorBlack) tagCursorStyle = tagStyle. Background(tcell.NewHexColor(0x8888cc)). Foreground(tcell.ColorBlack) @@ -47,7 +52,7 @@ func initStyles() error { tagSquareModifiedStyle = tagStyle. Background(tcell.NewHexColor(0x2222cc)) - vertlineStyle = bodyStyle + vertlineStyle = bodyStyle.Reverse(false) return nil } diff --git a/ui/tcell/tcell.go b/ui/tcell/tcell.go index ff20628..1ebc0cc 100644 --- a/ui/tcell/tcell.go +++ b/ui/tcell/tcell.go @@ -3,6 +3,7 @@ package uitcell import ( "fmt" "path/filepath" + "strings" "github.com/gdamore/tcell" "github.com/prodhe/poe/editor" @@ -17,14 +18,17 @@ const ( var ( screen tcell.Screen ed editor.Editor - menu *View workspace *Workspace CurWin *Window + poecmds map[string]commandFunc + quit chan bool events chan tcell.Event ) +type commandFunc func() + type Tcell struct{} func (t *Tcell) Init(e editor.Editor) error { @@ -37,10 +41,6 @@ func (t *Tcell) Init(e editor.Editor) error { return err } - if err := initMenu(); err != nil { - return err - } - if err := initWorkspace(); err != nil { return err } @@ -49,6 +49,8 @@ func (t *Tcell) Init(e editor.Editor) error { return err } + initCommands() + quit = make(chan bool, 1) events = make(chan tcell.Event, 100) @@ -65,14 +67,10 @@ func (t *Tcell) Close() { func printMsg(format string, a ...interface{}) { // get output window - var poewin *Window - for _, win := range AllWindows() { - poename := win.Dir() + string(filepath.Separator) + FnMessageWin - poename = filepath.Clean(poename) - if win.Name() == poename && CurWin.Dir() == win.Dir() { - poewin = win - } - } + poename := CurWin.Dir() + string(filepath.Separator) + FnMessageWin + poename = filepath.Clean(poename) + + poewin := FindWindow(poename) if poewin == nil { id, buf := ed.NewBuffer() @@ -110,19 +108,6 @@ func initScreen() error { return nil } -func initMenu() error { - menu = &View{ - text: &editor.Buffer{}, - what: ViewMenu, - style: bodyStyle, - cursorStyle: bodyCursorStyle, - hilightStyle: bodyHilightStyle, - tabstop: 4, - } - fmt.Fprintf(menu, "Exit New Newcol") - return nil -} - func initWorkspace() error { workspace = &Workspace{} // first resize event will set proper dimensions workspace.AddCol() @@ -134,12 +119,21 @@ func initWindows() error { for _, id := range ids { win := NewWindow(id) workspace.LastCol().AddWindow(win) + CurWin = win } return nil } +func initCommands() { + poecmds = map[string]commandFunc{ + "New": CmdNew, + "Newcol": CmdNewcol, + "Del": CmdDel, + "Exit": CmdExit, + } +} + func (t *Tcell) redraw() { - menu.Draw() workspace.Draw() screen.Show() } @@ -168,9 +162,7 @@ outer: switch e := event.(type) { case *tcell.EventResize: w, h := screen.Size() - menu.Resize(0, 0, w, 1) - workspace.Resize(0, 1, w, h-1) - //screen.Clear() + workspace.Resize(0, 0, w, h) screen.Sync() case *tcell.EventKey: // system wide shortcuts switch e.Key() { @@ -178,10 +170,6 @@ outer: screen.Clear() screen.Sync() default: // let the focused view handle event - if menu.focused { - menu.HandleEvent(e) - break - } if CurWin != nil { CurWin.HandleEvent(e) } @@ -198,15 +186,8 @@ outer: } } - // check if we are in the menu - menu.focused = false - if my < 1 { - menu.focused = true - menu.HandleEvent(e) - break - } - if CurWin != nil { + CurWin.Focus() CurWin.HandleEvent(e) } } @@ -220,6 +201,29 @@ outer: } } +func Cmd(input string) string { + if input == "" { + return "" + } + + input = strings.Trim(input, "\t\n ") + + // check poe default commands + cmd := strings.Split(string(input), " ") + if fn, ok := poecmds[cmd[0]]; ok { + fn() + return "" + } + + // Edit shortcuts for external commands and piping + switch input[0] { + case '!', '<', '>', '|': + return ed.Edit(CurWin.bufid, input) + } + + return ed.Edit(CurWin.bufid, "!"+input) +} + func CmdOpen(fn string) { screen.Clear() var win *Window @@ -233,7 +237,7 @@ func CmdOpen(fn string) { } } -func CmdNew(args string) { +func CmdNew() { screen.Clear() id, _ := ed.NewBuffer() win := NewWindow(id) @@ -241,9 +245,18 @@ func CmdNew(args string) { } func CmdDel() { + if len(AllWindows()) == 1 { + CmdExit() + return + } CurWin.Close() } +func CmdNewcol() { + workspace.AddCol() + CmdNew() +} + func CmdExit() { exit := true wins := AllWindows() @@ -252,7 +265,7 @@ func CmdExit() { exit = false } } - if exit || len(wins) == 0 { + if exit { quit <- true } } diff --git a/ui/tcell/view.go b/ui/tcell/view.go index ef42207..7c0eb64 100644 --- a/ui/tcell/view.go +++ b/ui/tcell/view.go @@ -33,7 +33,6 @@ type View struct { tabstop int focused bool what int - dirty bool // modified since last read/write mclicktime time.Time // last mouse click in time mclickpos int // byte offset accounting for runes mpressed bool @@ -44,9 +43,6 @@ func (v *View) Write(p []byte) (int, error) { if err != nil { return 0, err } - if v.what == ViewBody { - v.dirty = true - } v.SetCursor(0, 1) // force scroll if needed return n, err } @@ -62,9 +58,6 @@ func (v *View) Delete() (int, error) { if err != nil { return n, err } - if v.what == ViewBody { - v.dirty = true - } v.SetCursor(0, 1) // force scroll return n, nil } @@ -79,11 +72,17 @@ func (v *View) Rune() rune { return r } +// Cursor returns start of dot. func (v *View) Cursor() int { q0, _ := v.text.Dot() return q0 } +// Dirty returns true if the buffer has been written to. +func (v *View) Dirty() bool { + return v.text.Dirty() +} + func (v *View) SetCursor(pos, whence int) { v.text.SeekDot(pos, whence) @@ -235,11 +234,15 @@ func (v *View) ScrollTo(offset int) { } func (b *View) Draw() { + //screen.HideCursor() + x, y := b.x, b.y if b.text.Len() > 0 { b.opos = b.scrollpos // keep track of last visible char/overflow b.text.Seek(b.scrollpos, io.SeekStart) + q0, q1 := b.text.Dot() + for i := b.scrollpos; i < b.text.Len(); { // i gets incremented after reading of the rune, to know how many bytes we need to skip // line wrap if x > b.x+b.w { @@ -255,15 +258,18 @@ func (b *View) Draw() { // default style style := b.style - // highlight cursor - q0, q1 := b.text.Dot() + // highlight cursor if on screen if (q0 == q1 && i == q0) && b.focused { - style = b.cursorStyle + //style = b.cursorStyle + screen.ShowCursor(x, y) } - // highlight selection - if (i >= q0 && i < q1) && b.focused { + // highlight selection, even if not focused + if i >= q0 && i < q1 { style = b.hilightStyle + if b.focused { + screen.HideCursor() + } } // draw rune from buffer @@ -334,10 +340,16 @@ func (b *View) Draw() { } if y < b.y+b.h { screen.SetContent(x, y, ' ', nil, b.cursorStyle) + screen.ShowCursor(x, y) x++ } } + // if we are in focus, we are allowed to hide the central cursor if dot is currently off screen + if b.focused && (q0 < b.scrollpos || q0 > b.opos) { + screen.HideCursor() + } + // clear the rest and optionally show a special char as empty line for w := b.x + b.w; w >= x; w-- { screen.SetContent(w, y, ' ', nil, b.style) @@ -385,7 +397,10 @@ func (v *View) HandleEvent(ev tcell.Event) { // if we clicked inside a current selection, run that one q0, q1 := v.text.Dot() if pos >= q0 && pos <= q1 && q0 != q1 { - ed.Run(v.text.ReadDot()) + output := Cmd(v.text.ReadDot()) + if output != "" { + printMsg(output) + } return } @@ -393,9 +408,12 @@ func (v *View) HandleEvent(ev tcell.Event) { p := pos - v.text.PrevSpace(pos) n := pos + v.text.NextSpace(pos) v.text.SetDot(p, n) - fn := strings.Trim(v.text.ReadDot(), "\n\t ") + str := strings.Trim(v.text.ReadDot(), "\n\t ") v.text.SetDot(q0, q1) - ed.Run(fn) + output := Cmd(str) + if output != "" { + printMsg(output) + } return } @@ -445,7 +463,7 @@ func (v *View) HandleEvent(ev tcell.Event) { // if we clicked inside a current selection, run that one q0, q1 := v.text.Dot() if pos >= q0 && pos <= q1 && q0 != q1 { - ed.Run(v.text.ReadDot()) + Cmd(v.text.ReadDot()) return } @@ -455,7 +473,7 @@ func (v *View) HandleEvent(ev tcell.Event) { v.text.SetDot(p, n) fn := strings.Trim(v.text.ReadDot(), "\n\t ") v.text.SetDot(q0, q1) - ed.Run(fn) + Cmd(fn) return case tcell.Button3: // right click pos := v.XYToOffset(mx, my) @@ -562,10 +580,12 @@ func (v *View) HandleEvent(ev tcell.Event) { v.Delete() return case tcell.KeyCtrlG: // file info/statistics - printMsg("0x%.4x %q len %d\nbasedir: %s\nwindir: %s\nname: %s\n", + sw, sh := screen.Size() + printMsg("0x%.4x %q len %d\nbasedir: %s\nwindir: %s\nname: %s\nw: %d h: %d sw: %d sh: %d\n", v.Rune(), v.Rune(), v.text.Len(), - ed.WorkDir(), CurWin.Dir(), CurWin.Name()) + ed.WorkDir(), CurWin.Dir(), CurWin.Name(), + CurWin.w, CurWin.h, sh, sw) return case tcell.KeyCtrlO: // open file/dir fn := v.text.ReadDot() @@ -586,6 +606,9 @@ func (v *View) HandleEvent(ev tcell.Event) { } CmdOpen(fn) return + case tcell.KeyCtrlN: // new column + CmdNewcol() + return case tcell.KeyCtrlR: // run command in dot cmd := v.text.ReadDot() if cmd == "" { // select all non-space characters @@ -599,7 +622,8 @@ func (v *View) HandleEvent(ev tcell.Event) { return } } - ed.Run(cmd) + res := Cmd(cmd) + printMsg("%s\n", res) return case tcell.KeyCtrlC: // copy to clipboard str := v.text.ReadDot() @@ -618,13 +642,7 @@ func (v *View) HandleEvent(ev tcell.Event) { } v.text.Write([]byte(s)) return - case tcell.KeyCtrlQ: - // close entire application if we are in the top menu - if v.what == ViewMenu { - CmdExit() - return - } - // otherwise, just close this window (CurWin) + case tcell.KeyCtrlQ: // close window CmdDel() return default: diff --git a/ui/tcell/window.go b/ui/tcell/window.go index fcb146c..b3774d2 100644 --- a/ui/tcell/window.go +++ b/ui/tcell/window.go @@ -14,9 +14,8 @@ type Window struct { body *View tagline *View col *Column // reference to the column where in - hidden bool - collapsed bool // not implemented - qcnt int // quit count + collapsed bool // not implemented + qcnt int // quit count } // NewWindow returns a fresh window associated with the given filename. For special case filenames, look at the package constants. @@ -75,8 +74,13 @@ func (win *Window) Resize(x, y, w, h int) { win.body.Resize(win.x, win.y+1, win.w, win.h-1) } -func (win *Window) UnFocus() { +func (win *Window) Focus() { win.body.focused = true + //win.tagline.focused = true +} + +func (win *Window) UnFocus() { + win.body.focused = false win.tagline.focused = false } @@ -85,7 +89,22 @@ func (win *Window) Name() string { } func (win *Window) Dir() string { - return win.body.text.WorkDir() + d := win.body.text.WorkDir() + if d == "." { + return ed.WorkDir() // base path for program + } + return d +} + +func (win *Window) Flags() [2]rune { + flags := [2]rune{' ', '-'} + if win.body.Dirty() { + flags[0] = '\'' + } + if win.collapsed { + flags[1] = '+' + } + return flags } func (win *Window) HandleEvent(ev tcell.Event) { @@ -119,13 +138,9 @@ func (win *Window) HandleEvent(ev tcell.Event) { } func (win *Window) Draw() { - // Draw tag square - boxstyle := tagSquareStyle - if win.body.dirty { - boxstyle = tagSquareModifiedStyle - } - screen.SetContent(win.x, win.y, ' ', nil, boxstyle) - screen.SetContent(win.x+1, win.y, ' ', nil, boxstyle) + flags := win.Flags() + screen.SetContent(win.x, win.y, flags[0], nil, win.tagline.style) + screen.SetContent(win.x+1, win.y, flags[1], nil, win.tagline.style) screen.SetContent(win.x+2, win.y, ' ', nil, win.tagline.style) // Tagline @@ -136,11 +151,14 @@ func (win *Window) Draw() { } func (win *Window) CanClose() bool { - ok := !win.body.dirty || win.qcnt > 0 + if win.body.what == ViewScratch { + return true + } + ok := (!win.body.Dirty() || win.qcnt > 0) if !ok { name := win.Name() if name == FnEmptyWin { - name = "unnamed file" + name = "unnamed buffer" } printMsg("%s modified\n", name) win.qcnt++ diff --git a/ui/ui.go b/ui/ui.go index 6dc843a..9f742bf 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -9,14 +9,7 @@ const ( SignalQuit int = iota ) -type Messager interface { - // Message prints output from the editor, using the Printf signature. - Message(format string, a ...interface{}) -} - type Interface interface { - //Messager - // Init initializes the user interface. Init(ed editor.Editor) error -- cgit v1.2.3