mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-07-05 00:59:19 +02:00
use tcell via porting over code from awesome-gocui
This commit is contained in:
56
vendor/github.com/jesseduffield/gocui/CHANGES_tcell.md
generated
vendored
Normal file
56
vendor/github.com/jesseduffield/gocui/CHANGES_tcell.md
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
# Change from termbox to tcell
|
||||
|
||||
Original GOCUI was written on top of [termbox](https://github.com/nsf/termbox-go) package. This document describes changes which were done to be able to use to [tcell/v2](https://github.com/gdamore/tcell) package.
|
||||
|
||||
## Attribute color
|
||||
|
||||
Attribute type represents a terminal attribute like color and font effects. Color and font effects can be combined using bitwise OR (`|`).
|
||||
|
||||
In `termbox` colors were represented by range 1 to 256. `0` was default color which uses the terminal default setting.
|
||||
|
||||
In `tcell` colors can be represented in 24bit, and all of them starts from 0. Valid colors have special flag which gives them real value starting from 4294967296. `0` is a default similart to `termbox`.
|
||||
The change to support all these colors was made in a way, that original colors from 1 to 256 are backward compatible and if user has color specified as
|
||||
`Attribute(ansicolor+1)` without the valid color flag, it will be translated to `tcell` color by subtracting 1 and making the color valid by adding the flag. This should ensure backward compatibility.
|
||||
|
||||
All the color constants are the same with different underlying values. From user perspective, this should be fine unless some arithmetic is done with it. For example `ColorBlack` was `1` in original version but is `4294967296` in new version.
|
||||
|
||||
GOCUI provides a few helper functions which could be used to get the real color value or to create a color attribute.
|
||||
|
||||
- `(a Attribute).Hex()` - returns `int32` value of the color represented as `Red << 16 | Green << 8 | Blue`
|
||||
- `(a Attribute).RGB()` - returns 3 `int32` values for red, green and blue color.
|
||||
- `GetColor(string)` - creates `Attribute` from color passed as a string. This can be hex value or color name (W3C name).
|
||||
- `Get256Color(int32)` - creates `Attribute` from color number (ANSI colors).
|
||||
- `GetRGBColor(int32)` - creates `Attribute` from color number created the same way as `Hex()` function returns.
|
||||
- `NewRGBColor(int32, int32, int32)` - creates `Attribute` from color numbers for red, green and blue values.
|
||||
|
||||
## Attribute font effect
|
||||
|
||||
There were 3 attributes for font effect, `AttrBold`, `AttrUnderline` and `AttrReverse`.
|
||||
|
||||
`tcell` supports more attributes, so they were added. All of these attributes have different values from before. However they can be used in the same way as before.
|
||||
|
||||
All the font effect attributes:
|
||||
- `AttrBold`
|
||||
- `AttrBlink`
|
||||
- `AttrReverse`
|
||||
- `AttrUnderline`
|
||||
- `AttrDim`
|
||||
- `AttrItalic`
|
||||
- `AttrStrikeThrough`
|
||||
|
||||
## OutputMode
|
||||
|
||||
`OutputMode` in `termbox` was used to translate colors into the correct range. So for example in `OutputGrayscale` you had colors from 1 - 24 all representing gray colors in range 232 - 255, and white and black color.
|
||||
|
||||
`tcell` colors are 24bit and they are translated by the library into the color which can be read by terminal.
|
||||
|
||||
The original translation from `termbox` was included in GOCUI to be backward compatible. This is enabled in all the original modes: `OutputNormal`, `Output216`, `OutputGrayscale` and `Output256`.
|
||||
|
||||
`OutputTrue` is a new mode. It is recomended, because in this mode GOCUI doesn't do any kind of translation of the colors and pass them directly to `tcell`. If user wants to use true color in terminal and this mode doesn't work, it might be because of the terminal setup. `tcell` has a documentation what needs to be done, but in short `COLORTERM=truecolor` environment variable should help (see [_examples/colorstrue.go](./_examples/colorstrue.go)). Other way would be to have `TERM` environment variable having value with suffix `-truecolor`. To disable true color set `TCELL_TRUECOLOR=disable`.
|
||||
|
||||
## Keybinding
|
||||
|
||||
`termbox` had different way of handling input from terminal than `tcell`. This leads to some adjustement on how the keys are represented.
|
||||
In general, all the keys in GOCUI should be presented from before, but the underlying values might be different. This could lead to some problems if a user uses different parser to create the `Key` for the keybinding. If using GOCUI parser, everything should be ok.
|
||||
|
||||
Mouse is handled differently in `tcell`, but translation was done to keep it in the same way as it was before. However this was harder to test due to different behaviour across the platforms, so if anything is missing or not working, please report.
|
76
vendor/github.com/jesseduffield/gocui/CODE_OF_CONDUCT.md
generated
vendored
Normal file
76
vendor/github.com/jesseduffield/gocui/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at mkopenga@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
33
vendor/github.com/jesseduffield/gocui/CONTRIBUTING.md
generated
vendored
Normal file
33
vendor/github.com/jesseduffield/gocui/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
# Contributing
|
||||
|
||||
Everyone is welcome to help make gocui better!
|
||||
|
||||
When contributing to this repository, please first discuss the change you wish
|
||||
to make via issue, email, or any other method with the owners of this repository
|
||||
before making a change.
|
||||
|
||||
## So all code changes happen through Pull Requests
|
||||
Pull requests are the best way to propose changes to the codebase. We actively
|
||||
welcome your pull requests:
|
||||
|
||||
1. Fork the repo and create your branch from `master` with a name like `feature/contributors-guide`.
|
||||
2. If you've added code that should be tested, add tests.
|
||||
3. If you've added code that need documentation, update the documentation.
|
||||
4. Make sure your code follows the [effective go](https://golang.org/doc/effective_go.html) guidelines as much as possible.
|
||||
5. Be sure to test your modifications.
|
||||
6. Make sure your branch is up to date with the master branch.
|
||||
7. Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||
8. Create that pull request!
|
||||
|
||||
## Code of conduct
|
||||
Please note by participating in this project, you agree to abide by the [code of conduct].
|
||||
|
||||
[code of conduct]: https://github.com/awesome-gocui/gocui/blob/master/CODE-OF-CONDUCT.md
|
||||
|
||||
## Any contributions you make will be under the license indicated in the [license](LICENSE.md)
|
||||
In short, when you submit code changes, your submissions are understood to be
|
||||
under the same license as the rest of project. Feel free to contact the maintainers if that's a concern.
|
||||
|
||||
## Report bugs using Github's [issues](https://github.com/awesome-gocui/gocui/issues)
|
||||
We use GitHub issues to track public bugs. Report a bug by [opening a new
|
||||
issue](https://github.com/awesome-gocui/gocui/issues/new); it's that easy!
|
51
vendor/github.com/jesseduffield/gocui/README.md
generated
vendored
51
vendor/github.com/jesseduffield/gocui/README.md
generated
vendored
@ -1,8 +1,13 @@
|
||||
# GOCUI - Go Console User Interface
|
||||
|
||||
[](https://godoc.org/github.com/jroimartin/gocui)
|
||||
[](https://circleci.com/gh/awesome-gocui/gocui/tree/master)
|
||||
[](https://codecov.io/gh/awesome-gocui/gocui)
|
||||
[](https://goreportcard.com/report/github.com/awesome-gocui/gocui)
|
||||
[](https://golangci.com/badges/github.com/awesome-gocui/gocui.svg)
|
||||
[](https://godoc.org/github.com/awesome-gocui/gocui)
|
||||

|
||||
|
||||
Minimalist Go package aimed at creating Console User Interfaces.
|
||||
A community fork based on the amazing work of [jroimartin](https://github.com/jroimartin/gocui)
|
||||
|
||||
## Features
|
||||
|
||||
@ -13,15 +18,30 @@ Minimalist Go package aimed at creating Console User Interfaces.
|
||||
* Global and view-level keybindings.
|
||||
* Mouse support.
|
||||
* Colored text.
|
||||
* Customizable edition mode.
|
||||
* Customizable editing mode.
|
||||
* Easy to build reusable widgets, complex layouts...
|
||||
|
||||
## About fork
|
||||
|
||||
This fork has many improvements over the original work from [jroimartin](https://github.com/jroimartin/gocui).
|
||||
|
||||
* Written ontop of TCell
|
||||
* Better wide character support
|
||||
* Support for 1 Line height views
|
||||
* Better support for running in docker container
|
||||
* Customize frame colors
|
||||
* Improved code comments and quality
|
||||
* Many small improvements
|
||||
* Change Visibility of views
|
||||
|
||||
For information about this org see: [awesome-gocui/about](https://github.com/awesome-gocui/about).
|
||||
|
||||
## Installation
|
||||
|
||||
Execute:
|
||||
|
||||
```
|
||||
$ go get github.com/jroimartin/gocui
|
||||
$ go get github.com/awesome-gocui/gocui
|
||||
```
|
||||
|
||||
## Documentation
|
||||
@ -29,13 +49,14 @@ $ go get github.com/jroimartin/gocui
|
||||
Execute:
|
||||
|
||||
```
|
||||
$ go doc github.com/jroimartin/gocui
|
||||
$ go doc github.com/awesome-gocui/gocui
|
||||
```
|
||||
|
||||
Or visit [godoc.org](https://godoc.org/github.com/jroimartin/gocui) to read it
|
||||
Or visit [godoc.org](https://godoc.org/github.com/awesome-gocui/gocui) to read it
|
||||
online.
|
||||
|
||||
## Example
|
||||
See the [_example](./_example/) folder for more examples
|
||||
|
||||
```go
|
||||
package main
|
||||
@ -44,11 +65,11 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/awesome-gocui/gocui"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
@ -60,19 +81,25 @@ func main() {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
if err := g.MainLoop(); err != nil && !gocui.IsQuit(err) {
|
||||
log.Panicln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func layout(g *gocui.Gui) error {
|
||||
maxX, maxY := g.Size()
|
||||
if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
if v, err := g.SetView("hello", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2, 0); err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := g.SetCurrentView("hello"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(v, "Hello world!")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -106,5 +133,7 @@ func quit(g *gocui.Gui, v *gocui.View) error {
|
||||
* [fac](https://github.com/mkchoi212/fac): git merge conflict resolver
|
||||
* [jsonui](https://github.com/gulyasm/jsonui): Interactive JSON explorer for your terminal.
|
||||
* [cointop](https://github.com/miguelmota/cointop): Interactive terminal based UI application for tracking cryptocurrencies.
|
||||
* [lazygit](https://github.com/jesseduffield/lazygit): simple terminal UI for git commands.
|
||||
* [lazydocker](https://github.com/jesseduffield/lazydocker): The lazier way to manage everything docker.
|
||||
|
||||
Note: if your project is not listed here, let us know! :)
|
||||
|
173
vendor/github.com/jesseduffield/gocui/attribute.go
generated
vendored
173
vendor/github.com/jesseduffield/gocui/attribute.go
generated
vendored
@ -1,32 +1,165 @@
|
||||
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||
// Copyright 2020 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 gocui
|
||||
|
||||
import "github.com/jesseduffield/termbox-go"
|
||||
import "github.com/gdamore/tcell/v2"
|
||||
|
||||
// Attribute represents a terminal attribute, like color, font style, etc. They
|
||||
// can be combined using bitwise OR (|). Note that it is not possible to
|
||||
// combine multiple color attributes.
|
||||
type Attribute termbox.Attribute
|
||||
// Attribute affects the presentation of characters, such as color, boldness, etc.
|
||||
type Attribute uint64
|
||||
|
||||
// Color attributes.
|
||||
const (
|
||||
ColorDefault Attribute = Attribute(termbox.ColorDefault)
|
||||
ColorBlack = Attribute(termbox.ColorBlack)
|
||||
ColorRed = Attribute(termbox.ColorRed)
|
||||
ColorGreen = Attribute(termbox.ColorGreen)
|
||||
ColorYellow = Attribute(termbox.ColorYellow)
|
||||
ColorBlue = Attribute(termbox.ColorBlue)
|
||||
ColorMagenta = Attribute(termbox.ColorMagenta)
|
||||
ColorCyan = Attribute(termbox.ColorCyan)
|
||||
ColorWhite = Attribute(termbox.ColorWhite)
|
||||
// ColorDefault is used to leave the Color unchanged from whatever system or teminal default may exist.
|
||||
ColorDefault = Attribute(tcell.ColorDefault)
|
||||
|
||||
// AttrIsValidColor is used to indicate the color value is actually
|
||||
// valid (initialized). This is useful to permit the zero value
|
||||
// to be treated as the default.
|
||||
AttrIsValidColor = Attribute(tcell.ColorValid)
|
||||
|
||||
// AttrIsRGBColor is used to indicate that the Attribute value is RGB value of color.
|
||||
// The lower order 3 bytes are RGB.
|
||||
// (It's not a color in basic ANSI range 256).
|
||||
AttrIsRGBColor = Attribute(tcell.ColorIsRGB)
|
||||
|
||||
// AttrColorBits is a mask where color is located in Attribute
|
||||
AttrColorBits = 0xffffffffff // roughly 5 bytes, tcell uses 4 bytes and half-byte as a special flags for color (rest is reserved for future)
|
||||
|
||||
// AttrStyleBits is a mask where character attributes (e.g.: bold, italic, underline) are located in Attribute
|
||||
AttrStyleBits = 0xffffff0000000000 // remaining 3 bytes in the 8 bytes Attribute (tcell is not using it, so we should be fine)
|
||||
)
|
||||
|
||||
// Text style attributes.
|
||||
// Color attributes. These colors are compatible with tcell.Color type and can be expanded like:
|
||||
// g.FgColor := gocui.Attribute(tcell.ColorLime)
|
||||
const (
|
||||
AttrBold Attribute = Attribute(termbox.AttrBold)
|
||||
AttrUnderline = Attribute(termbox.AttrUnderline)
|
||||
AttrReverse = Attribute(termbox.AttrReverse)
|
||||
ColorBlack Attribute = AttrIsValidColor + iota
|
||||
ColorRed
|
||||
ColorGreen
|
||||
ColorYellow
|
||||
ColorBlue
|
||||
ColorMagenta
|
||||
ColorCyan
|
||||
ColorWhite
|
||||
)
|
||||
|
||||
// grayscale indexes (for backward compatibility with termbox-go original grayscale)
|
||||
var grayscale = []tcell.Color{
|
||||
16, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
|
||||
245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 231,
|
||||
}
|
||||
|
||||
// Attributes are not colors, but effects (e.g.: bold, dim) which affect the display of text.
|
||||
// They can be combined.
|
||||
const (
|
||||
AttrBold Attribute = 1 << (40 + iota)
|
||||
AttrBlink
|
||||
AttrReverse
|
||||
AttrUnderline
|
||||
AttrDim
|
||||
AttrItalic
|
||||
AttrStrikeThrough
|
||||
AttrNone Attribute = 0 // Just normal text.
|
||||
)
|
||||
|
||||
// AttrAll represents all the text effect attributes turned on
|
||||
const AttrAll = AttrBold | AttrBlink | AttrReverse | AttrUnderline | AttrDim | AttrItalic
|
||||
|
||||
// IsValidColor indicates if the Attribute is a valid color value (has been set).
|
||||
func (a Attribute) IsValidColor() bool {
|
||||
return a&AttrIsValidColor != 0
|
||||
}
|
||||
|
||||
// Hex returns the color's hexadecimal RGB 24-bit value with each component
|
||||
// consisting of a single byte, ala R << 16 | G << 8 | B. If the color
|
||||
// is unknown or unset, -1 is returned.
|
||||
//
|
||||
// This function produce the same output as `tcell.Hex()` with additional
|
||||
// support for `termbox-go` colors (to 256).
|
||||
func (a Attribute) Hex() int32 {
|
||||
if !a.IsValidColor() {
|
||||
return -1
|
||||
}
|
||||
tc := getTcellColor(a, OutputTrue)
|
||||
return tc.Hex()
|
||||
}
|
||||
|
||||
// RGB returns the red, green, and blue components of the color, with
|
||||
// each component represented as a value 0-255. If the color
|
||||
// is unknown or unset, -1 is returned for each component.
|
||||
//
|
||||
// This function produce the same output as `tcell.RGB()` with additional
|
||||
// support for `termbox-go` colors (to 256).
|
||||
func (a Attribute) RGB() (int32, int32, int32) {
|
||||
v := a.Hex()
|
||||
if v < 0 {
|
||||
return -1, -1, -1
|
||||
}
|
||||
return (v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff
|
||||
}
|
||||
|
||||
// GetColor creates a Color from a color name (W3C name). A hex value may
|
||||
// be supplied as a string in the format "#ffffff".
|
||||
func GetColor(color string) Attribute {
|
||||
return Attribute(tcell.GetColor(color))
|
||||
}
|
||||
|
||||
// Get256Color creates Attribute which stores ANSI color (0-255)
|
||||
func Get256Color(color int32) Attribute {
|
||||
return Attribute(color) | AttrIsValidColor
|
||||
}
|
||||
|
||||
// GetRGBColor creates Attribute which stores RGB color.
|
||||
// Color is passed as 24bit RGB value, where R << 16 | G << 8 | B
|
||||
func GetRGBColor(color int32) Attribute {
|
||||
return Attribute(color) | AttrIsValidColor | AttrIsRGBColor
|
||||
}
|
||||
|
||||
// NewRGBColor creates Attribute which stores RGB color.
|
||||
func NewRGBColor(r, g, b int32) Attribute {
|
||||
return Attribute(tcell.NewRGBColor(r, g, b))
|
||||
}
|
||||
|
||||
// getTcellColor transform Attribute into tcell.Color
|
||||
func getTcellColor(c Attribute, omode OutputMode) tcell.Color {
|
||||
c = c & AttrColorBits
|
||||
// Default color is 0 in tcell/v2 and was 0 in termbox-go, so we are good here
|
||||
if c == ColorDefault {
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
|
||||
tc := tcell.ColorDefault
|
||||
// Check if we have valid color
|
||||
if c.IsValidColor() {
|
||||
tc = tcell.Color(c)
|
||||
} else if c > 0 && c <= 256 {
|
||||
// It's not valid color, but it has value in range 1-256
|
||||
// This is old Attribute style of color from termbox-go (black=1, etc.)
|
||||
// convert to tcell color (black=0|ColorValid)
|
||||
tc = tcell.Color(c-1) | tcell.ColorValid
|
||||
}
|
||||
|
||||
switch omode {
|
||||
case OutputTrue:
|
||||
return tc
|
||||
case OutputNormal:
|
||||
tc &= tcell.Color(0xf) | tcell.ColorValid
|
||||
case Output256:
|
||||
tc &= tcell.Color(0xff) | tcell.ColorValid
|
||||
case Output216:
|
||||
tc &= tcell.Color(0xff)
|
||||
if tc > 215 {
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
tc += tcell.Color(16) | tcell.ColorValid
|
||||
case OutputGrayscale:
|
||||
tc &= tcell.Color(0x1f)
|
||||
if tc > 26 {
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
tc = grayscale[tc] | tcell.ColorValid
|
||||
default:
|
||||
return tcell.ColorDefault
|
||||
}
|
||||
return tc
|
||||
}
|
||||
|
12
vendor/github.com/jesseduffield/gocui/doc.go
generated
vendored
12
vendor/github.com/jesseduffield/gocui/doc.go
generated
vendored
@ -7,7 +7,7 @@ Package gocui allows to create console user interfaces.
|
||||
|
||||
Create a new GUI:
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputNormal)
|
||||
g, err := gocui.NewGui(gocui.OutputNormal, false)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
@ -16,7 +16,7 @@ Create a new GUI:
|
||||
// Set GUI managers and key bindings
|
||||
// ...
|
||||
|
||||
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
||||
if err := g.MainLoop(); err != nil && !gocui.IsQuit(err) {
|
||||
// handle error
|
||||
}
|
||||
|
||||
@ -37,8 +37,8 @@ their content. The same is valid for reading.
|
||||
|
||||
Create and initialize a view with absolute coordinates:
|
||||
|
||||
if v, err := g.SetView("viewname", 2, 2, 22, 7); err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
if v, err := g.SetView("viewname", 2, 2, 22, 7, 0); err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
// handle error
|
||||
}
|
||||
fmt.Fprintln(v, "This is a new view")
|
||||
@ -48,7 +48,7 @@ Create and initialize a view with absolute coordinates:
|
||||
Views can also be created using relative coordinates:
|
||||
|
||||
maxX, maxY := g.Size()
|
||||
if v, err := g.SetView("viewname", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil {
|
||||
if v, err := g.SetView("viewname", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2, 0); err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ use *Gui.Update(). For example:
|
||||
return nil
|
||||
})
|
||||
|
||||
By default, gocui provides a basic edition mode. This mode can be extended
|
||||
By default, gocui provides a basic editing mode. This mode can be extended
|
||||
and customized creating a new Editor and assigning it to *View.Editor:
|
||||
|
||||
type Editor interface {
|
||||
|
27
vendor/github.com/jesseduffield/gocui/edit.go
generated
vendored
27
vendor/github.com/jesseduffield/gocui/edit.go
generated
vendored
@ -33,10 +33,16 @@ var DefaultEditor Editor = EditorFunc(simpleEditor)
|
||||
// simpleEditor is used as the default gocui editor.
|
||||
func simpleEditor(v *View, key Key, ch rune, mod Modifier) {
|
||||
switch {
|
||||
case ch != 0 && mod == 0:
|
||||
v.EditWrite(ch)
|
||||
case key == KeySpace:
|
||||
v.EditWrite(' ')
|
||||
case key == KeyBackspace || key == KeyBackspace2:
|
||||
v.EditDelete(true)
|
||||
case key == KeyDelete:
|
||||
v.EditDelete(false)
|
||||
case key == KeyInsert:
|
||||
v.Overwrite = !v.Overwrite
|
||||
case key == KeyArrowDown:
|
||||
v.MoveCursor(0, 1, false)
|
||||
case key == KeyArrowUp:
|
||||
@ -73,7 +79,7 @@ func (v *View) EditWrite(ch rune) {
|
||||
v.moveCursor(w, 0, true)
|
||||
}
|
||||
|
||||
// EditDeleteToStartOfLine is the equivalent of pressing ctrl+U in your terminal, it deletes to the end of the line. Or if you are already at the start of the line, it deletes the newline character
|
||||
// EditDeleteToStartOfLine is the equivalent of pressing ctrl+U in your terminal, it deletes to the start of the line. Or if you are already at the start of the line, it deletes the newline character
|
||||
func (v *View) EditDeleteToStartOfLine() {
|
||||
x, _ := v.Cursor()
|
||||
if x == 0 {
|
||||
@ -350,18 +356,17 @@ func (v *View) writeRune(x, y int, ch rune) error {
|
||||
}
|
||||
|
||||
olen := len(v.lines[y])
|
||||
w := runewidth.RuneWidth(ch)
|
||||
|
||||
var s []cell
|
||||
if x >= len(v.lines[y]) {
|
||||
s = make([]cell, x-len(v.lines[y])+w)
|
||||
s = make([]cell, x-len(v.lines[y])+1)
|
||||
} else if !v.Overwrite {
|
||||
s = make([]cell, w)
|
||||
s = make([]cell, 1)
|
||||
}
|
||||
v.lines[y] = append(v.lines[y], s...)
|
||||
|
||||
if !v.Overwrite || (v.Overwrite && x >= olen-w) {
|
||||
copy(v.lines[y][x+w:], v.lines[y][x:])
|
||||
if !v.Overwrite || (v.Overwrite && x >= olen-1) {
|
||||
copy(v.lines[y][x+1:], v.lines[y][x:])
|
||||
}
|
||||
v.lines[y][x] = cell{
|
||||
fgColor: v.FgColor,
|
||||
@ -369,14 +374,6 @@ func (v *View) writeRune(x, y int, ch rune) error {
|
||||
chr: ch,
|
||||
}
|
||||
|
||||
for i := 1; i < w; i++ {
|
||||
v.lines[y][x+i] = cell{
|
||||
fgColor: v.FgColor,
|
||||
bgColor: v.BgColor,
|
||||
chr: '\x00',
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -403,7 +400,7 @@ func (v *View) deleteRune(x, y int) (int, error) {
|
||||
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+w:]...)
|
||||
v.lines[y] = append(v.lines[y][:i], v.lines[y][i+1:]...)
|
||||
return w, nil
|
||||
}
|
||||
|
||||
|
190
vendor/github.com/jesseduffield/gocui/escape.go
generated
vendored
190
vendor/github.com/jesseduffield/gocui/escape.go
generated
vendored
@ -24,6 +24,11 @@ const (
|
||||
ERASE_IN_LINE
|
||||
)
|
||||
|
||||
type (
|
||||
escapeState int
|
||||
fontEffect int
|
||||
)
|
||||
|
||||
type instruction struct {
|
||||
kind int
|
||||
param1 int
|
||||
@ -31,13 +36,21 @@ type instruction struct {
|
||||
toWrite []rune
|
||||
}
|
||||
|
||||
type escapeState int
|
||||
|
||||
const (
|
||||
stateNone escapeState = iota
|
||||
stateEscape
|
||||
stateCSI
|
||||
stateParams
|
||||
|
||||
bold fontEffect = 1
|
||||
faint fontEffect = 2
|
||||
italic fontEffect = 3
|
||||
underline fontEffect = 4
|
||||
blink fontEffect = 5
|
||||
reverse fontEffect = 7
|
||||
strike fontEffect = 9
|
||||
setForegroundColor fontEffect = 38
|
||||
setBackgroundColor fontEffect = 48
|
||||
)
|
||||
|
||||
var (
|
||||
@ -144,6 +157,8 @@ func (ei *escapeInterpreter) parseOne(ch rune) (isEscape bool, err error) {
|
||||
err = ei.outputNormal()
|
||||
case Output256:
|
||||
err = ei.output256()
|
||||
case OutputTrue:
|
||||
err = ei.outputTrue()
|
||||
}
|
||||
if err != nil {
|
||||
return false, errCSIParseError
|
||||
@ -181,22 +196,18 @@ func (ei *escapeInterpreter) outputNormal() error {
|
||||
|
||||
switch {
|
||||
case p >= 30 && p <= 37:
|
||||
ei.curFgColor |= Attribute(p - 30 + 1)
|
||||
ei.curFgColor = Get256Color(int32(p) - 30)
|
||||
case p == 39:
|
||||
ei.curFgColor |= ColorDefault
|
||||
ei.curFgColor = ColorDefault
|
||||
case p >= 40 && p <= 47:
|
||||
ei.curBgColor |= Attribute(p - 40 + 1)
|
||||
ei.curBgColor = Get256Color(int32(p) - 40)
|
||||
case p == 49:
|
||||
ei.curBgColor |= ColorDefault
|
||||
case p == 1:
|
||||
ei.curFgColor |= AttrBold
|
||||
case p == 4:
|
||||
ei.curFgColor |= AttrUnderline
|
||||
case p == 7:
|
||||
ei.curFgColor |= AttrReverse
|
||||
ei.curBgColor = ColorDefault
|
||||
case p == 0:
|
||||
ei.curFgColor = ColorDefault
|
||||
ei.curBgColor = ColorDefault
|
||||
default:
|
||||
ei.curFgColor |= getFontEffect(p)
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,39 +232,130 @@ func (ei *escapeInterpreter) output256() error {
|
||||
return ei.outputNormal()
|
||||
}
|
||||
|
||||
fgbg, err := strconv.Atoi(ei.csiParam[0])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
color, err := strconv.Atoi(ei.csiParam[2])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
|
||||
switch fgbg {
|
||||
case 38:
|
||||
ei.curFgColor = Attribute(color + 1)
|
||||
|
||||
for _, param := range ei.csiParam[3:] {
|
||||
p, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
|
||||
switch {
|
||||
case p == 1:
|
||||
ei.curFgColor |= AttrBold
|
||||
case p == 4:
|
||||
ei.curFgColor |= AttrUnderline
|
||||
case p == 7:
|
||||
ei.curFgColor |= AttrReverse
|
||||
}
|
||||
for _, param := range splitFgBg(ei.csiParam, 3) {
|
||||
fgbg, err := strconv.Atoi(param[0])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
color, err := strconv.Atoi(param[2])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
case 48:
|
||||
ei.curBgColor = Attribute(color + 1)
|
||||
default:
|
||||
return errCSIParseError
|
||||
}
|
||||
|
||||
switch fontEffect(fgbg) {
|
||||
case setForegroundColor:
|
||||
ei.curFgColor = Get256Color(int32(color))
|
||||
|
||||
for _, s := range param[3:] {
|
||||
p, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
|
||||
ei.curFgColor |= getFontEffect(p)
|
||||
}
|
||||
case setBackgroundColor:
|
||||
ei.curBgColor = Get256Color(int32(color))
|
||||
default:
|
||||
return errCSIParseError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// outputTrue allows you to leverage the true-color terminal mode.
|
||||
//
|
||||
// Works with rgb ANSI sequence: `\x1b[38;2;<r>;<g>;<b>m`, `\x1b[48;2;<r>;<g>;<b>m`
|
||||
func (ei *escapeInterpreter) outputTrue() error {
|
||||
if len(ei.csiParam) < 5 {
|
||||
return ei.output256()
|
||||
}
|
||||
|
||||
mode, err := strconv.Atoi(ei.csiParam[1])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
if mode != 2 {
|
||||
return ei.output256()
|
||||
}
|
||||
|
||||
for _, param := range splitFgBg(ei.csiParam, 5) {
|
||||
fgbg, err := strconv.Atoi(param[0])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
colr, err := strconv.Atoi(param[2])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
colg, err := strconv.Atoi(param[3])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
colb, err := strconv.Atoi(param[4])
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
color := NewRGBColor(int32(colr), int32(colg), int32(colb))
|
||||
|
||||
switch fontEffect(fgbg) {
|
||||
case setForegroundColor:
|
||||
ei.curFgColor = color
|
||||
|
||||
for _, s := range param[5:] {
|
||||
p, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return errCSIParseError
|
||||
}
|
||||
|
||||
ei.curFgColor |= getFontEffect(p)
|
||||
}
|
||||
case setBackgroundColor:
|
||||
ei.curBgColor = color
|
||||
default:
|
||||
return errCSIParseError
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// splitFgBg splits foreground and background color according to ANSI sequence.
|
||||
//
|
||||
// num (number of segments in ansi) is used to determine if it's 256 mode or rgb mode (3 - 256-color, 5 - rgb-color)
|
||||
func splitFgBg(params []string, num int) [][]string {
|
||||
var out [][]string
|
||||
var current []string
|
||||
for _, p := range params {
|
||||
if len(current) == num && (p == "48" || p == "38") {
|
||||
out = append(out, current)
|
||||
current = []string{}
|
||||
}
|
||||
current = append(current, p)
|
||||
}
|
||||
|
||||
if len(current) > 0 {
|
||||
out = append(out, current)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func getFontEffect(f int) Attribute {
|
||||
switch fontEffect(f) {
|
||||
case bold:
|
||||
return AttrBold
|
||||
case faint:
|
||||
return AttrDim
|
||||
case italic:
|
||||
return AttrItalic
|
||||
case underline:
|
||||
return AttrUnderline
|
||||
case blink:
|
||||
return AttrBlink
|
||||
case reverse:
|
||||
return AttrReverse
|
||||
case strike:
|
||||
return AttrStrikeThrough
|
||||
}
|
||||
return AttrNone
|
||||
}
|
||||
|
10
vendor/github.com/jesseduffield/gocui/go.mod
generated
vendored
Normal file
10
vendor/github.com/jesseduffield/gocui/go.mod
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
module github.com/jesseduffield/gocui
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/gdamore/tcell/v2 v2.0.0
|
||||
github.com/go-errors/errors v1.0.2
|
||||
github.com/mattn/go-runewidth v0.0.9
|
||||
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00
|
||||
)
|
17
vendor/github.com/jesseduffield/gocui/go.sum
generated
vendored
Normal file
17
vendor/github.com/jesseduffield/gocui/go.sum
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.0.0 h1:GRWG8aLfWAlekj9Q6W29bVvkHENc6hp79XOqG4AWDOs=
|
||||
github.com/gdamore/tcell/v2 v2.0.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
|
||||
github.com/go-errors/errors v1.0.2 h1:xMxH9j2fNg/L4hLn/4y3M0IUsn0M6Wbu/Uh9QlOfBh4=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00 h1:Rl8NelBe+n7SuLbJyw13ho7CGWUt2BjGGKIoreCWQ/c=
|
||||
github.com/nsf/termbox-go v0.0.0-20201124104050-ed494de23a00/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo=
|
||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
|
||||
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
337
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
337
vendor/github.com/jesseduffield/gocui/gui.go
generated
vendored
@ -7,38 +7,57 @@ package gocui
|
||||
import (
|
||||
standardErrors "errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
|
||||
"github.com/jesseduffield/termbox-go"
|
||||
)
|
||||
|
||||
// OutputMode represents an output mode, which determines how colors
|
||||
// are used.
|
||||
type OutputMode int
|
||||
|
||||
var (
|
||||
// ErrQuit is used to decide if the MainLoop finished successfully.
|
||||
ErrQuit = standardErrors.New("quit")
|
||||
// ErrAlreadyBlacklisted is returned when the keybinding is already blacklisted.
|
||||
ErrAlreadyBlacklisted = standardErrors.New("keybind already blacklisted")
|
||||
|
||||
// ErrBlacklisted is returned when the keybinding being parsed / used is blacklisted.
|
||||
ErrBlacklisted = standardErrors.New("keybind blacklisted")
|
||||
|
||||
// ErrNotBlacklisted is returned when a keybinding being whitelisted is not blacklisted.
|
||||
ErrNotBlacklisted = standardErrors.New("keybind not blacklisted")
|
||||
|
||||
// ErrNoSuchKeybind is returned when the keybinding being parsed does not exist.
|
||||
ErrNoSuchKeybind = standardErrors.New("no such keybind")
|
||||
|
||||
// ErrUnknownView allows to assert if a View must be initialized.
|
||||
ErrUnknownView = standardErrors.New("unknown view")
|
||||
)
|
||||
|
||||
// OutputMode represents the terminal's output mode (8 or 256 colors).
|
||||
type OutputMode termbox.OutputMode
|
||||
// ErrQuit is used to decide if the MainLoop finished successfully.
|
||||
ErrQuit = standardErrors.New("quit")
|
||||
)
|
||||
|
||||
const (
|
||||
// OutputNormal provides 8-colors terminal mode.
|
||||
OutputNormal = OutputMode(termbox.OutputNormal)
|
||||
OutputNormal OutputMode = iota
|
||||
|
||||
// Output256 provides 256-colors terminal mode.
|
||||
Output256 = OutputMode(termbox.Output256)
|
||||
Output256
|
||||
|
||||
// OutputGrayScale provides greyscale terminal mode.
|
||||
OutputGrayScale = OutputMode(termbox.OutputGrayscale)
|
||||
// Output216 provides 216 ansi color terminal mode.
|
||||
Output216
|
||||
|
||||
// Output216 provides greyscale terminal mode.
|
||||
Output216 = OutputMode(termbox.Output216)
|
||||
// OutputGrayscale provides greyscale terminal mode.
|
||||
OutputGrayscale
|
||||
|
||||
// OutputTrue provides 24bit color terminal mode.
|
||||
// This mode is recommended even if your terminal doesn't support
|
||||
// such mode. The colors are represented exactly as you
|
||||
// write them (no clamping or truncating). `tcell` should take care
|
||||
// of what your terminal can do.
|
||||
OutputTrue
|
||||
)
|
||||
|
||||
type tabClickHandler func(int) error
|
||||
@ -59,30 +78,30 @@ type GuiMutexes struct {
|
||||
// Gui represents the whole User Interface, including the views, layouts
|
||||
// and keybindings.
|
||||
type Gui struct {
|
||||
tbEvents chan termbox.Event
|
||||
userEvents chan userEvent
|
||||
|
||||
// ReplayedEvents is a channel for passing pre-recorded input events, for the purposes of testing
|
||||
ReplayedEvents chan termbox.Event
|
||||
ReplayedEvents chan GocuiEvent
|
||||
RecordEvents bool
|
||||
RecordedEvents chan *termbox.Event
|
||||
RecordedEvents chan *GocuiEvent
|
||||
|
||||
tabClickBindings []*tabClickBinding
|
||||
gEvents chan GocuiEvent
|
||||
userEvents chan userEvent
|
||||
views []*View
|
||||
currentView *View
|
||||
managers []Manager
|
||||
keybindings []*keybinding
|
||||
tabClickBindings []*tabClickBinding
|
||||
maxX, maxY int
|
||||
outputMode OutputMode
|
||||
stop chan struct{}
|
||||
blacklist []Key
|
||||
|
||||
// BgColor and FgColor allow to configure the background and foreground
|
||||
// colors of the GUI.
|
||||
BgColor, FgColor Attribute
|
||||
BgColor, FgColor, FrameColor Attribute
|
||||
|
||||
// SelBgColor and SelFgColor allow to configure the background and
|
||||
// foreground colors of the frame of the current view.
|
||||
SelBgColor, SelFgColor Attribute
|
||||
SelBgColor, SelFgColor, SelFrameColor Attribute
|
||||
|
||||
// If Highlight is true, Sel{Bg,Fg}Colors will be used to draw the
|
||||
// frame of the current view.
|
||||
@ -117,29 +136,33 @@ type Gui struct {
|
||||
|
||||
// NewGui returns a new Gui object with a given output mode.
|
||||
func NewGui(mode OutputMode, supportOverlaps bool, recordEvents bool) (*Gui, error) {
|
||||
err := tcellInit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g := &Gui{}
|
||||
|
||||
var err error
|
||||
if g.maxX, g.maxY, err = g.getTermWindowSize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := termbox.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.outputMode = mode
|
||||
termbox.SetOutputMode(termbox.OutputMode(mode))
|
||||
|
||||
g.stop = make(chan struct{}, 0)
|
||||
g.stop = make(chan struct{})
|
||||
|
||||
g.tbEvents = make(chan termbox.Event, 20)
|
||||
g.ReplayedEvents = make(chan termbox.Event)
|
||||
g.ReplayedEvents = make(chan GocuiEvent)
|
||||
g.gEvents = make(chan GocuiEvent, 20)
|
||||
g.userEvents = make(chan userEvent, 20)
|
||||
g.RecordedEvents = make(chan *termbox.Event)
|
||||
g.RecordedEvents = make(chan *GocuiEvent)
|
||||
|
||||
g.BgColor, g.FgColor = ColorDefault, ColorDefault
|
||||
g.SelBgColor, g.SelFgColor = ColorDefault, ColorDefault
|
||||
if runtime.GOOS != "windows" {
|
||||
g.maxX, g.maxY, err = g.getTermWindowSize()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
g.maxX, g.maxY = screen.Size()
|
||||
}
|
||||
|
||||
g.BgColor, g.FgColor, g.FrameColor = ColorDefault, ColorDefault, ColorDefault
|
||||
g.SelBgColor, g.SelFgColor, g.SelFrameColor = ColorDefault, ColorDefault, ColorDefault
|
||||
|
||||
// SupportOverlaps is true when we allow for view edges to overlap with other
|
||||
// view edges
|
||||
@ -158,8 +181,10 @@ func NewGui(mode OutputMode, supportOverlaps bool, recordEvents bool) (*Gui, err
|
||||
// Close finalizes the library. It should be called after a successful
|
||||
// initialization and when gocui is not needed anymore.
|
||||
func (g *Gui) Close() {
|
||||
close(g.stop)
|
||||
termbox.Close()
|
||||
go func() {
|
||||
g.stop <- struct{}{}
|
||||
}()
|
||||
screen.Fini()
|
||||
}
|
||||
|
||||
// Size returns the terminal's size.
|
||||
@ -175,7 +200,7 @@ func (g *Gui) SetRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
|
||||
// swallowing error because it's not that big of a deal
|
||||
return nil
|
||||
}
|
||||
termbox.SetCell(x, y, ch, termbox.Attribute(fgColor), termbox.Attribute(bgColor))
|
||||
tcellSetCell(x, y, ch, fgColor, bgColor, g.outputMode)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -185,8 +210,8 @@ func (g *Gui) Rune(x, y int) (rune, error) {
|
||||
if x < 0 || y < 0 || x >= g.maxX || y >= g.maxY {
|
||||
return ' ', errors.New("invalid point")
|
||||
}
|
||||
c := termbox.CellBuffer()[y*g.maxX+x]
|
||||
return c.Ch, nil
|
||||
c, _, _, _ := screen.GetContent(x, y)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// SetView creates a new view with its top-left corner at (x0, y0)
|
||||
@ -290,11 +315,7 @@ func (g *Gui) ViewByPosition(x, y int) (*View, error) {
|
||||
// traverse views in reverse order checking top views first
|
||||
for i := len(g.views); i > 0; i-- {
|
||||
v := g.views[i-1]
|
||||
frameOffset := 0
|
||||
if v.Frame {
|
||||
frameOffset = 1
|
||||
}
|
||||
if x > v.x0-frameOffset && x < v.x1+frameOffset && y > v.y0-frameOffset && y < v.y1+frameOffset {
|
||||
if x > v.x0 && x < v.x1 && y > v.y0 && y < v.y1 {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
@ -304,9 +325,6 @@ func (g *Gui) ViewByPosition(x, y int) (*View, error) {
|
||||
// ViewPosition returns the coordinates of the view with the given name, or
|
||||
// error ErrUnknownView if a view with that name does not exist.
|
||||
func (g *Gui) ViewPosition(name string) (x0, y0, x1, y1 int, err error) {
|
||||
g.Mutexes.ViewsMutex.Lock()
|
||||
defer g.Mutexes.ViewsMutex.Unlock()
|
||||
|
||||
for _, v := range g.views {
|
||||
if v.name == name {
|
||||
return v.x0, v.y0, v.x1, v.y1, nil
|
||||
@ -317,9 +335,6 @@ func (g *Gui) ViewPosition(name string) (x0, y0, x1, y1 int, err error) {
|
||||
|
||||
// DeleteView deletes a view by name.
|
||||
func (g *Gui) DeleteView(name string) error {
|
||||
g.Mutexes.ViewsMutex.Lock()
|
||||
defer g.Mutexes.ViewsMutex.Unlock()
|
||||
|
||||
for i, v := range g.views {
|
||||
if v.name == name {
|
||||
g.views = append(g.views[:i], g.views[i+1:]...)
|
||||
@ -352,6 +367,11 @@ func (g *Gui) CurrentView() *View {
|
||||
// SetKeybinding creates a new keybinding. If viewname equals to ""
|
||||
// (empty string) then the keybinding will apply to all views. key must
|
||||
// be a rune or a Key.
|
||||
//
|
||||
// When mouse keys are used (MouseLeft, MouseRight, ...), modifier might not work correctly.
|
||||
// It behaves differently on different platforms. Somewhere it doesn't register Alt key press,
|
||||
// on others it might report Ctrl as Alt. It's not consistent and therefore it's not recommended
|
||||
// to use with mouse keys.
|
||||
func (g *Gui) SetKeybinding(viewname string, contexts []string, key interface{}, mod Modifier, handler func(*Gui, *View) error) error {
|
||||
var kb *keybinding
|
||||
|
||||
@ -359,6 +379,11 @@ func (g *Gui) SetKeybinding(viewname string, contexts []string, key interface{},
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if g.isBlacklisted(k) {
|
||||
return ErrBlacklisted
|
||||
}
|
||||
|
||||
kb = newKeybinding(viewname, contexts, k, ch, mod, handler)
|
||||
g.keybindings = append(g.keybindings, kb)
|
||||
return nil
|
||||
@ -401,6 +426,28 @@ func (g *Gui) SetTabClickBinding(viewName string, handler tabClickHandler) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// BlackListKeybinding adds a keybinding to the blacklist
|
||||
func (g *Gui) BlacklistKeybinding(k Key) error {
|
||||
for _, j := range g.blacklist {
|
||||
if j == k {
|
||||
return ErrAlreadyBlacklisted
|
||||
}
|
||||
}
|
||||
g.blacklist = append(g.blacklist, k)
|
||||
return nil
|
||||
}
|
||||
|
||||
// WhiteListKeybinding removes a keybinding from the blacklist
|
||||
func (g *Gui) WhitelistKeybinding(k Key) error {
|
||||
for i, j := range g.blacklist {
|
||||
if j == k {
|
||||
g.blacklist = append(g.blacklist[:i], g.blacklist[i+1:]...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrNotBlacklisted
|
||||
}
|
||||
|
||||
// getKey takes an empty interface with a key and returns the corresponding
|
||||
// typed Key or rune.
|
||||
func getKey(key interface{}) (Key, rune, error) {
|
||||
@ -425,7 +472,14 @@ type userEvent struct {
|
||||
// the user events queue. Given that Update spawns a goroutine, the order in
|
||||
// which the user events will be handled is not guaranteed.
|
||||
func (g *Gui) Update(f func(*Gui) error) {
|
||||
go func() { g.userEvents <- userEvent{f: f} }()
|
||||
go g.UpdateAsync(f)
|
||||
}
|
||||
|
||||
// UpdateAsync is a version of Update that does not spawn a go routine, it can
|
||||
// be a bit more efficient in cases where Update is called many times like when
|
||||
// tailing a file. In general you should use Update()
|
||||
func (g *Gui) UpdateAsync(f func(*Gui) error) {
|
||||
g.userEvents <- userEvent{f: f}
|
||||
}
|
||||
|
||||
// A Manager is in charge of GUI's layout and can be used to build widgets.
|
||||
@ -454,7 +508,7 @@ func (g *Gui) SetManager(managers ...Manager) {
|
||||
g.keybindings = nil
|
||||
g.tabClickBindings = nil
|
||||
|
||||
go func() { g.tbEvents <- termbox.Event{Type: termbox.EventResize} }()
|
||||
go func() { g.gEvents <- GocuiEvent{Type: eventResize} }()
|
||||
}
|
||||
|
||||
// SetManagerFunc sets the given manager function. It deletes all views and
|
||||
@ -476,26 +530,21 @@ func (g *Gui) MainLoop() error {
|
||||
case <-g.stop:
|
||||
return
|
||||
default:
|
||||
g.tbEvents <- termbox.PollEvent()
|
||||
g.gEvents <- pollEvent()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
inputMode := termbox.InputAlt
|
||||
if true { // previously g.InputEsc, but didn't seem to work
|
||||
inputMode = termbox.InputEsc
|
||||
}
|
||||
if g.Mouse {
|
||||
inputMode |= termbox.InputMouse
|
||||
screen.EnableMouse()
|
||||
}
|
||||
termbox.SetInputMode(inputMode)
|
||||
|
||||
if err := g.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case ev := <-g.tbEvents:
|
||||
case ev := <-g.gEvents:
|
||||
if err := g.handleEvent(&ev); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -521,7 +570,7 @@ func (g *Gui) MainLoop() error {
|
||||
func (g *Gui) consumeevents() error {
|
||||
for {
|
||||
select {
|
||||
case ev := <-g.tbEvents:
|
||||
case ev := <-g.gEvents:
|
||||
if err := g.handleEvent(&ev); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -541,16 +590,15 @@ func (g *Gui) consumeevents() error {
|
||||
|
||||
// handleEvent handles an event, based on its type (key-press, error,
|
||||
// etc.)
|
||||
func (g *Gui) handleEvent(ev *termbox.Event) error {
|
||||
if g.RecordEvents {
|
||||
g.RecordedEvents <- ev
|
||||
}
|
||||
|
||||
func (g *Gui) handleEvent(ev *GocuiEvent) error {
|
||||
switch ev.Type {
|
||||
case termbox.EventKey, termbox.EventMouse:
|
||||
case eventKey, eventMouse:
|
||||
return g.onKey(ev)
|
||||
case termbox.EventError:
|
||||
case eventError:
|
||||
return ev.Err
|
||||
// Not sure if this should be handled. It acts weirder when it's here
|
||||
// case eventResize:
|
||||
// return Sync()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -558,9 +606,9 @@ func (g *Gui) handleEvent(ev *termbox.Event) error {
|
||||
|
||||
// flush updates the gui, re-drawing frames and buffers.
|
||||
func (g *Gui) flush() error {
|
||||
termbox.Clear(termbox.Attribute(g.FgColor), termbox.Attribute(g.BgColor))
|
||||
g.clear(g.FgColor, g.BgColor)
|
||||
|
||||
maxX, maxY := termbox.Size()
|
||||
maxX, maxY := screen.Size()
|
||||
// if GUI's size has changed, we need to redraw all views
|
||||
if maxX != g.maxX || maxY != g.maxY {
|
||||
for _, v := range g.views {
|
||||
@ -575,16 +623,33 @@ func (g *Gui) flush() error {
|
||||
}
|
||||
}
|
||||
for _, v := range g.views {
|
||||
if v.y1 < v.y0 {
|
||||
if !v.Visible || v.y1 < v.y0 {
|
||||
continue
|
||||
}
|
||||
if v.Frame {
|
||||
fgColor, bgColor := g.viewColors(v)
|
||||
var fgColor, bgColor, frameColor Attribute
|
||||
if g.Highlight && v == g.currentView {
|
||||
fgColor = g.SelFgColor
|
||||
bgColor = g.SelBgColor
|
||||
frameColor = g.SelFrameColor
|
||||
} else {
|
||||
bgColor = g.BgColor
|
||||
if v.TitleColor != ColorDefault {
|
||||
fgColor = v.TitleColor
|
||||
} else {
|
||||
fgColor = g.FgColor
|
||||
}
|
||||
if v.FrameColor != ColorDefault {
|
||||
frameColor = v.FrameColor
|
||||
} else {
|
||||
frameColor = g.FrameColor
|
||||
}
|
||||
}
|
||||
|
||||
if err := g.drawFrameEdges(v, fgColor, bgColor); err != nil {
|
||||
if err := g.drawFrameEdges(v, frameColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.drawFrameCorners(v, fgColor, bgColor); err != nil {
|
||||
if err := g.drawFrameCorners(v, frameColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
if v.Title != "" || len(v.Tabs) > 0 {
|
||||
@ -607,15 +672,19 @@ func (g *Gui) flush() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
termbox.Flush()
|
||||
screen.Show()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Gui) viewColors(v *View) (Attribute, Attribute) {
|
||||
if g.Highlight && v == g.currentView {
|
||||
return g.SelFgColor, g.SelBgColor
|
||||
func (g *Gui) clear(fg, bg Attribute) (int, int) {
|
||||
st := getTcellStyle(fg, bg, g.outputMode)
|
||||
w, h := screen.Size()
|
||||
for row := 0; row < h; row++ {
|
||||
for col := 0; col < w; col++ {
|
||||
screen.SetContent(col, row, ' ', nil, st)
|
||||
}
|
||||
}
|
||||
return g.FgColor, g.BgColor
|
||||
return w, h
|
||||
}
|
||||
|
||||
// drawFrameEdges draws the horizontal and vertical edges of a view.
|
||||
@ -623,6 +692,8 @@ func (g *Gui) drawFrameEdges(v *View, fgColor, bgColor Attribute) error {
|
||||
runeH, runeV := '─', '│'
|
||||
if g.ASCII {
|
||||
runeH, runeV = '-', '|'
|
||||
} else if len(v.FrameRunes) >= 2 {
|
||||
runeH, runeV = v.FrameRunes[0], v.FrameRunes[1]
|
||||
}
|
||||
|
||||
for x := v.x0 + 1; x < v.x1 && x < g.maxX; x++ {
|
||||
@ -662,8 +733,64 @@ func cornerRune(index byte) rune {
|
||||
return []rune{' ', '│', '│', '│', '─', '┘', '┐', '┤', '─', '└', '┌', '├', '├', '┴', '┬', '┼'}[index]
|
||||
}
|
||||
|
||||
// cornerCustomRune returns rune from `v.FrameRunes` slice. If the length of slice is less than 11
|
||||
// all the missing runes will be translated to the default `cornerRune()`
|
||||
func cornerCustomRune(v *View, index byte) rune {
|
||||
// Translate `cornerRune()` index
|
||||
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
// ' ', '│', '│', '│', '─', '┘', '┐', '┤', '─', '└', '┌', '├', '├', '┴', '┬', '┼'
|
||||
// into `FrameRunes` index
|
||||
// 0 1 2 3 4 5 6 7 8 9 10
|
||||
// '─', '│', '┌', '┐', '└', '┘', '├', '┤', '┬', '┴', '┼'
|
||||
switch index {
|
||||
case 1, 2, 3:
|
||||
return v.FrameRunes[1]
|
||||
case 4, 8:
|
||||
return v.FrameRunes[0]
|
||||
case 5:
|
||||
return v.FrameRunes[5]
|
||||
case 6:
|
||||
return v.FrameRunes[3]
|
||||
case 7:
|
||||
if len(v.FrameRunes) < 8 {
|
||||
break
|
||||
}
|
||||
return v.FrameRunes[7]
|
||||
case 9:
|
||||
return v.FrameRunes[4]
|
||||
case 10:
|
||||
return v.FrameRunes[2]
|
||||
case 11, 12:
|
||||
if len(v.FrameRunes) < 7 {
|
||||
break
|
||||
}
|
||||
return v.FrameRunes[6]
|
||||
case 13:
|
||||
if len(v.FrameRunes) < 10 {
|
||||
break
|
||||
}
|
||||
return v.FrameRunes[9]
|
||||
case 14:
|
||||
if len(v.FrameRunes) < 9 {
|
||||
break
|
||||
}
|
||||
return v.FrameRunes[8]
|
||||
case 15:
|
||||
if len(v.FrameRunes) < 11 {
|
||||
break
|
||||
}
|
||||
return v.FrameRunes[10]
|
||||
default:
|
||||
return ' ' // cornerRune(0)
|
||||
}
|
||||
return cornerRune(index)
|
||||
}
|
||||
|
||||
func corner(v *View, directions byte) rune {
|
||||
index := v.Overlaps | directions
|
||||
if len(v.FrameRunes) >= 6 {
|
||||
return cornerCustomRune(v, index)
|
||||
}
|
||||
return cornerRune(index)
|
||||
}
|
||||
|
||||
@ -682,6 +809,9 @@ func (g *Gui) drawFrameCorners(v *View, fgColor, bgColor Attribute) error {
|
||||
}
|
||||
|
||||
runeTL, runeTR, runeBL, runeBR := '┌', '┐', '└', '┘'
|
||||
if len(v.FrameRunes) >= 6 {
|
||||
runeTL, runeTR, runeBL, runeBR = v.FrameRunes[2], v.FrameRunes[3], v.FrameRunes[4], v.FrameRunes[5]
|
||||
}
|
||||
if g.SupportOverlaps {
|
||||
runeTL = corner(v, BOTTOM|RIGHT)
|
||||
runeTR = corner(v, BOTTOM|LEFT)
|
||||
@ -743,7 +873,6 @@ func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error {
|
||||
} else if x > v.x1-2 || x >= g.maxX {
|
||||
break
|
||||
}
|
||||
|
||||
currentFgColor := fgColor
|
||||
currentBgColor := bgColor
|
||||
// if you are the current view and you have multiple tabs, de-highlight the non-selected tabs
|
||||
@ -765,7 +894,6 @@ func (g *Gui) drawTitle(v *View, fgColor, bgColor Attribute) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -837,14 +965,17 @@ func (g *Gui) draw(v *View) error {
|
||||
|
||||
gMaxX, gMaxY := g.Size()
|
||||
cx, cy := curview.x0+curview.cx+1, curview.y0+curview.cy+1
|
||||
// This test probably doesn't need to be here.
|
||||
// tcell is hiding cursor by setting coordinates outside of screen.
|
||||
// Keeping it here for now, as I'm not 100% sure :)
|
||||
if cx >= 0 && cx < gMaxX && cy >= 0 && cy < gMaxY {
|
||||
termbox.SetCursor(cx, cy)
|
||||
screen.ShowCursor(cx, cy)
|
||||
} else {
|
||||
termbox.HideCursor()
|
||||
screen.HideCursor()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
termbox.HideCursor()
|
||||
screen.HideCursor()
|
||||
}
|
||||
|
||||
v.clearRunes()
|
||||
@ -857,9 +988,9 @@ func (g *Gui) draw(v *View) error {
|
||||
// onKey manages key-press events. A keybinding handler is called when
|
||||
// a key-press or mouse event satisfies a configured keybinding. Furthermore,
|
||||
// currentView's internal buffer is modified if currentView.Editable is true.
|
||||
func (g *Gui) onKey(ev *termbox.Event) error {
|
||||
func (g *Gui) onKey(ev *GocuiEvent) error {
|
||||
switch ev.Type {
|
||||
case termbox.EventKey:
|
||||
case eventKey:
|
||||
matched, err := g.execKeybindings(g.currentView, ev)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -870,7 +1001,7 @@ func (g *Gui) onKey(ev *termbox.Event) error {
|
||||
if g.currentView != nil && g.currentView.Editable && g.currentView.Editor != nil {
|
||||
g.currentView.Editor.Edit(g.currentView, Key(ev.Key), ev.Ch, Modifier(ev.Mod))
|
||||
}
|
||||
case termbox.EventMouse:
|
||||
case eventMouse:
|
||||
mx, my := ev.MouseX, ev.MouseY
|
||||
v, err := g.ViewByPosition(mx, my)
|
||||
if err != nil {
|
||||
@ -911,7 +1042,7 @@ func (g *Gui) onKey(ev *termbox.Event) error {
|
||||
|
||||
// execKeybindings executes the keybinding handlers that match the passed view
|
||||
// and event. The value of matched is true if there is a match and no errors.
|
||||
func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err error) {
|
||||
func (g *Gui) execKeybindings(v *View, ev *GocuiEvent) (matched bool, err error) {
|
||||
var globalKb *keybinding
|
||||
var matchingParentViewKb *keybinding
|
||||
|
||||
@ -960,6 +1091,10 @@ func (g *Gui) execKeybindings(v *View, ev *termbox.Event) (matched bool, err err
|
||||
|
||||
// execKeybinding executes a given keybinding
|
||||
func (g *Gui) execKeybinding(v *View, kb *keybinding) (bool, error) {
|
||||
if g.isBlacklisted(kb.key) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if err := kb.handler(g, v); err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -989,3 +1124,23 @@ func (g *Gui) StartTicking() {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// isBlacklisted reports whether the key is blacklisted
|
||||
func (g *Gui) isBlacklisted(k Key) bool {
|
||||
for _, j := range g.blacklist {
|
||||
if j == k {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUnknownView reports whether the contents of an error is "unknown view".
|
||||
func IsUnknownView(err error) bool {
|
||||
return err != nil && err.Error() == ErrUnknownView.Error()
|
||||
}
|
||||
|
||||
// IsQuit reports whether the contents of an error is "quit".
|
||||
func IsQuit(err error) bool {
|
||||
return err != nil && err.Error() == ErrQuit.Error()
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
// 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.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package gocui
|
||||
@ -11,16 +15,17 @@ import (
|
||||
"github.com/go-errors/errors"
|
||||
)
|
||||
|
||||
type windowSize struct {
|
||||
rows uint16
|
||||
cols uint16
|
||||
xpixels uint16
|
||||
ypixels uint16
|
||||
}
|
||||
|
||||
// getTermWindowSize is get terminal window size on linux or unix.
|
||||
// When gocui run inside the docker contaienr need to check and get the window size.
|
||||
func (g *Gui) getTermWindowSize() (int, int, error) {
|
||||
var sz struct {
|
||||
rows uint16
|
||||
cols uint16
|
||||
_ [2]uint16 // to match underlying syscall; see https://github.com/awesome-gocui/gocui/issues/33
|
||||
}
|
||||
|
||||
var termw, termh int
|
||||
|
||||
out, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
@ -29,21 +34,15 @@ func (g *Gui) getTermWindowSize() (int, int, error) {
|
||||
|
||||
signalCh := make(chan os.Signal, 1)
|
||||
signal.Notify(signalCh, syscall.SIGWINCH, syscall.SIGINT)
|
||||
defer signal.Stop(signalCh)
|
||||
|
||||
var sz windowSize
|
||||
|
||||
for {
|
||||
_, _, err = syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
out.Fd(),
|
||||
uintptr(syscall.TIOCGWINSZ),
|
||||
uintptr(unsafe.Pointer(&sz)),
|
||||
)
|
||||
_, _, _ = syscall.Syscall(syscall.SYS_IOCTL,
|
||||
out.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&sz)))
|
||||
|
||||
// check terminal window size
|
||||
if sz.cols > 0 && sz.rows > 0 {
|
||||
return int(sz.cols), int(sz.rows), nil
|
||||
termw, termh = int(sz.cols), int(sz.rows)
|
||||
if termw > 0 && termh > 0 {
|
||||
return termw, termh, nil
|
||||
}
|
||||
|
||||
select {
|
||||
@ -54,7 +53,7 @@ func (g *Gui) getTermWindowSize() (int, int, error) {
|
||||
continue
|
||||
// ctrl + c to cancel
|
||||
case syscall.SIGINT:
|
||||
return 0, 0, errors.New("There was not enough window space to start the application")
|
||||
return 0, 0, errors.New("stop to get term window size")
|
||||
}
|
||||
}
|
||||
}
|
10
vendor/github.com/jesseduffield/gocui/gui_win.go
generated
vendored
10
vendor/github.com/jesseduffield/gocui/gui_win.go
generated
vendored
@ -1,10 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package gocui
|
||||
|
||||
import "github.com/jesseduffield/termbox-go"
|
||||
|
||||
func (g *Gui) getTermWindowSize() (int, int, error) {
|
||||
x, y := termbox.Size()
|
||||
return x, y, nil
|
||||
}
|
53
vendor/github.com/jesseduffield/gocui/gui_windows.go
generated
vendored
Normal file
53
vendor/github.com/jesseduffield/gocui/gui_windows.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// 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.
|
||||
|
||||
// +build windows
|
||||
|
||||
package gocui
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type wchar uint16
|
||||
type short int16
|
||||
type dword uint32
|
||||
type word uint16
|
||||
|
||||
type coord struct {
|
||||
x short
|
||||
y short
|
||||
}
|
||||
|
||||
type smallRect struct {
|
||||
left short
|
||||
top short
|
||||
right short
|
||||
bottom short
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
size coord
|
||||
cursorPosition coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize coord
|
||||
}
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
)
|
||||
|
||||
// getTermWindowSize is get terminal window size on windows.
|
||||
func (g *Gui) getTermWindowSize() (int, int, error) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(os.Stdout.Fd(), uintptr(unsafe.Pointer(&csbi)))
|
||||
if r1 == 0 {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1), nil
|
||||
}
|
326
vendor/github.com/jesseduffield/gocui/keybinding.go
generated
vendored
326
vendor/github.com/jesseduffield/gocui/keybinding.go
generated
vendored
@ -4,7 +4,18 @@
|
||||
|
||||
package gocui
|
||||
|
||||
import "github.com/jesseduffield/termbox-go"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
// Key represents special keys or keys combinations.
|
||||
type Key tcell.Key
|
||||
|
||||
// Modifier allows to define special keys combinations. They can be used
|
||||
// in combination with Keys or Runes when a new keybinding is defined.
|
||||
type Modifier tcell.ModMask
|
||||
|
||||
// Keybidings are used to link a given key-press event with a handler.
|
||||
type keybinding struct {
|
||||
@ -16,6 +27,71 @@ type keybinding struct {
|
||||
handler func(*Gui, *View) error
|
||||
}
|
||||
|
||||
// Parse takes the input string and extracts the keybinding.
|
||||
// Returns a Key / rune, a Modifier and an error.
|
||||
func Parse(input string) (interface{}, Modifier, error) {
|
||||
if len(input) == 1 {
|
||||
_, r, err := getKey(rune(input[0]))
|
||||
if err != nil {
|
||||
return nil, ModNone, err
|
||||
}
|
||||
return r, ModNone, nil
|
||||
}
|
||||
|
||||
var modifier Modifier
|
||||
cleaned := make([]string, 0)
|
||||
|
||||
tokens := strings.Split(input, "+")
|
||||
for _, t := range tokens {
|
||||
normalized := strings.Title(strings.ToLower(t))
|
||||
if t == "Alt" {
|
||||
modifier = ModAlt
|
||||
continue
|
||||
}
|
||||
cleaned = append(cleaned, normalized)
|
||||
}
|
||||
|
||||
key, exist := translate[strings.Join(cleaned, "")]
|
||||
if !exist {
|
||||
return nil, ModNone, ErrNoSuchKeybind
|
||||
}
|
||||
|
||||
return key, modifier, nil
|
||||
}
|
||||
|
||||
// ParseAll takes an array of strings and returns a map of all keybindings.
|
||||
func ParseAll(input []string) (map[interface{}]Modifier, error) {
|
||||
ret := make(map[interface{}]Modifier)
|
||||
for _, i := range input {
|
||||
k, m, err := Parse(i)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret[k] = m
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// MustParse takes the input string and returns a Key / rune and a Modifier.
|
||||
// It will panic if any error occured.
|
||||
func MustParse(input string) (interface{}, Modifier) {
|
||||
k, m, err := Parse(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return k, m
|
||||
}
|
||||
|
||||
// MustParseAll takes an array of strings and returns a map of all keybindings.
|
||||
// It will panic if any error occured.
|
||||
func MustParseAll(input []string) map[interface{}]Modifier {
|
||||
result, err := ParseAll(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// newKeybinding returns a new Keybinding object.
|
||||
func newKeybinding(viewname string, contexts []string, key Key, ch rune, mod Modifier, handler func(*Gui, *View) error) (kb *keybinding) {
|
||||
kb = &keybinding{
|
||||
@ -29,7 +105,7 @@ func newKeybinding(viewname string, contexts []string, key Key, ch rune, mod Mod
|
||||
return kb
|
||||
}
|
||||
|
||||
func eventMatchesKey(ev *termbox.Event, key interface{}) bool {
|
||||
func eventMatchesKey(ev *GocuiEvent, key interface{}) bool {
|
||||
// assuming ModNone for now
|
||||
if Modifier(ev.Mod) != ModNone {
|
||||
return false
|
||||
@ -72,99 +148,177 @@ func (kb *keybinding) matchView(v *View) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Key represents special keys or keys combinations.
|
||||
type Key termbox.Key
|
||||
// translations for strings to keys
|
||||
var translate = map[string]Key{
|
||||
"F1": KeyF1,
|
||||
"F2": KeyF2,
|
||||
"F3": KeyF3,
|
||||
"F4": KeyF4,
|
||||
"F5": KeyF5,
|
||||
"F6": KeyF6,
|
||||
"F7": KeyF7,
|
||||
"F8": KeyF8,
|
||||
"F9": KeyF9,
|
||||
"F10": KeyF10,
|
||||
"F11": KeyF11,
|
||||
"F12": KeyF12,
|
||||
"Insert": KeyInsert,
|
||||
"Delete": KeyDelete,
|
||||
"Home": KeyHome,
|
||||
"End": KeyEnd,
|
||||
"Pgup": KeyPgup,
|
||||
"Pgdn": KeyPgdn,
|
||||
"ArrowUp": KeyArrowUp,
|
||||
"ArrowDown": KeyArrowDown,
|
||||
"ArrowLeft": KeyArrowLeft,
|
||||
"ArrowRight": KeyArrowRight,
|
||||
"CtrlTilde": KeyCtrlTilde,
|
||||
"Ctrl2": KeyCtrl2,
|
||||
"CtrlSpace": KeyCtrlSpace,
|
||||
"CtrlA": KeyCtrlA,
|
||||
"CtrlB": KeyCtrlB,
|
||||
"CtrlC": KeyCtrlC,
|
||||
"CtrlD": KeyCtrlD,
|
||||
"CtrlE": KeyCtrlE,
|
||||
"CtrlF": KeyCtrlF,
|
||||
"CtrlG": KeyCtrlG,
|
||||
"Backspace": KeyBackspace,
|
||||
"CtrlH": KeyCtrlH,
|
||||
"Tab": KeyTab,
|
||||
"CtrlI": KeyCtrlI,
|
||||
"CtrlJ": KeyCtrlJ,
|
||||
"CtrlK": KeyCtrlK,
|
||||
"CtrlL": KeyCtrlL,
|
||||
"Enter": KeyEnter,
|
||||
"CtrlM": KeyCtrlM,
|
||||
"CtrlN": KeyCtrlN,
|
||||
"CtrlO": KeyCtrlO,
|
||||
"CtrlP": KeyCtrlP,
|
||||
"CtrlQ": KeyCtrlQ,
|
||||
"CtrlR": KeyCtrlR,
|
||||
"CtrlS": KeyCtrlS,
|
||||
"CtrlT": KeyCtrlT,
|
||||
"CtrlU": KeyCtrlU,
|
||||
"CtrlV": KeyCtrlV,
|
||||
"CtrlW": KeyCtrlW,
|
||||
"CtrlX": KeyCtrlX,
|
||||
"CtrlY": KeyCtrlY,
|
||||
"CtrlZ": KeyCtrlZ,
|
||||
"Esc": KeyEsc,
|
||||
"CtrlLsqBracket": KeyCtrlLsqBracket,
|
||||
"Ctrl3": KeyCtrl3,
|
||||
"Ctrl4": KeyCtrl4,
|
||||
"CtrlBackslash": KeyCtrlBackslash,
|
||||
"Ctrl5": KeyCtrl5,
|
||||
"CtrlRsqBracket": KeyCtrlRsqBracket,
|
||||
"Ctrl6": KeyCtrl6,
|
||||
"Ctrl7": KeyCtrl7,
|
||||
"CtrlSlash": KeyCtrlSlash,
|
||||
"CtrlUnderscore": KeyCtrlUnderscore,
|
||||
"Space": KeySpace,
|
||||
"Backspace2": KeyBackspace2,
|
||||
"Ctrl8": KeyCtrl8,
|
||||
"Mouseleft": MouseLeft,
|
||||
"Mousemiddle": MouseMiddle,
|
||||
"Mouseright": MouseRight,
|
||||
"Mouserelease": MouseRelease,
|
||||
"MousewheelUp": MouseWheelUp,
|
||||
"MousewheelDown": MouseWheelDown,
|
||||
}
|
||||
|
||||
// Special keys.
|
||||
const (
|
||||
KeyF1 Key = Key(termbox.KeyF1)
|
||||
KeyF2 = Key(termbox.KeyF2)
|
||||
KeyF3 = Key(termbox.KeyF3)
|
||||
KeyF4 = Key(termbox.KeyF4)
|
||||
KeyF5 = Key(termbox.KeyF5)
|
||||
KeyF6 = Key(termbox.KeyF6)
|
||||
KeyF7 = Key(termbox.KeyF7)
|
||||
KeyF8 = Key(termbox.KeyF8)
|
||||
KeyF9 = Key(termbox.KeyF9)
|
||||
KeyF10 = Key(termbox.KeyF10)
|
||||
KeyF11 = Key(termbox.KeyF11)
|
||||
KeyF12 = Key(termbox.KeyF12)
|
||||
KeyInsert = Key(termbox.KeyInsert)
|
||||
KeyDelete = Key(termbox.KeyDelete)
|
||||
KeyHome = Key(termbox.KeyHome)
|
||||
KeyEnd = Key(termbox.KeyEnd)
|
||||
KeyPgup = Key(termbox.KeyPgup)
|
||||
KeyPgdn = Key(termbox.KeyPgdn)
|
||||
KeyArrowUp = Key(termbox.KeyArrowUp)
|
||||
KeyArrowDown = Key(termbox.KeyArrowDown)
|
||||
KeyArrowLeft = Key(termbox.KeyArrowLeft)
|
||||
KeyArrowRight = Key(termbox.KeyArrowRight)
|
||||
|
||||
MouseLeft = Key(termbox.MouseLeft)
|
||||
MouseMiddle = Key(termbox.MouseMiddle)
|
||||
MouseRight = Key(termbox.MouseRight)
|
||||
MouseRelease = Key(termbox.MouseRelease)
|
||||
MouseWheelUp = Key(termbox.MouseWheelUp)
|
||||
MouseWheelDown = Key(termbox.MouseWheelDown)
|
||||
KeyF1 Key = Key(tcell.KeyF1)
|
||||
KeyF2 = Key(tcell.KeyF2)
|
||||
KeyF3 = Key(tcell.KeyF3)
|
||||
KeyF4 = Key(tcell.KeyF4)
|
||||
KeyF5 = Key(tcell.KeyF5)
|
||||
KeyF6 = Key(tcell.KeyF6)
|
||||
KeyF7 = Key(tcell.KeyF7)
|
||||
KeyF8 = Key(tcell.KeyF8)
|
||||
KeyF9 = Key(tcell.KeyF9)
|
||||
KeyF10 = Key(tcell.KeyF10)
|
||||
KeyF11 = Key(tcell.KeyF11)
|
||||
KeyF12 = Key(tcell.KeyF12)
|
||||
KeyInsert = Key(tcell.KeyInsert)
|
||||
KeyDelete = Key(tcell.KeyDelete)
|
||||
KeyHome = Key(tcell.KeyHome)
|
||||
KeyEnd = Key(tcell.KeyEnd)
|
||||
KeyPgdn = Key(tcell.KeyPgDn)
|
||||
KeyPgup = Key(tcell.KeyPgUp)
|
||||
KeyArrowUp = Key(tcell.KeyUp)
|
||||
KeyArrowDown = Key(tcell.KeyDown)
|
||||
KeyArrowLeft = Key(tcell.KeyLeft)
|
||||
KeyArrowRight = Key(tcell.KeyRight)
|
||||
)
|
||||
|
||||
// Keys combinations.
|
||||
const (
|
||||
KeyCtrlTilde Key = Key(termbox.KeyCtrlTilde)
|
||||
KeyCtrl2 = Key(termbox.KeyCtrl2)
|
||||
KeyCtrlSpace = Key(termbox.KeyCtrlSpace)
|
||||
KeyCtrlA = Key(termbox.KeyCtrlA)
|
||||
KeyCtrlB = Key(termbox.KeyCtrlB)
|
||||
KeyCtrlC = Key(termbox.KeyCtrlC)
|
||||
KeyCtrlD = Key(termbox.KeyCtrlD)
|
||||
KeyCtrlE = Key(termbox.KeyCtrlE)
|
||||
KeyCtrlF = Key(termbox.KeyCtrlF)
|
||||
KeyCtrlG = Key(termbox.KeyCtrlG)
|
||||
KeyBackspace = Key(termbox.KeyBackspace)
|
||||
KeyCtrlH = Key(termbox.KeyCtrlH)
|
||||
KeyTab = Key(termbox.KeyTab)
|
||||
KeyCtrlI = Key(termbox.KeyCtrlI)
|
||||
KeyCtrlJ = Key(termbox.KeyCtrlJ)
|
||||
KeyCtrlK = Key(termbox.KeyCtrlK)
|
||||
KeyCtrlL = Key(termbox.KeyCtrlL)
|
||||
KeyEnter = Key(termbox.KeyEnter)
|
||||
KeyCtrlM = Key(termbox.KeyCtrlM)
|
||||
KeyCtrlN = Key(termbox.KeyCtrlN)
|
||||
KeyCtrlO = Key(termbox.KeyCtrlO)
|
||||
KeyCtrlP = Key(termbox.KeyCtrlP)
|
||||
KeyCtrlQ = Key(termbox.KeyCtrlQ)
|
||||
KeyCtrlR = Key(termbox.KeyCtrlR)
|
||||
KeyCtrlS = Key(termbox.KeyCtrlS)
|
||||
KeyCtrlT = Key(termbox.KeyCtrlT)
|
||||
KeyCtrlU = Key(termbox.KeyCtrlU)
|
||||
KeyCtrlV = Key(termbox.KeyCtrlV)
|
||||
KeyCtrlW = Key(termbox.KeyCtrlW)
|
||||
KeyCtrlX = Key(termbox.KeyCtrlX)
|
||||
KeyCtrlY = Key(termbox.KeyCtrlY)
|
||||
KeyCtrlZ = Key(termbox.KeyCtrlZ)
|
||||
KeyEsc = Key(termbox.KeyEsc)
|
||||
KeyCtrlLsqBracket = Key(termbox.KeyCtrlLsqBracket)
|
||||
KeyCtrl3 = Key(termbox.KeyCtrl3)
|
||||
KeyCtrl4 = Key(termbox.KeyCtrl4)
|
||||
KeyCtrlBackslash = Key(termbox.KeyCtrlBackslash)
|
||||
KeyCtrl5 = Key(termbox.KeyCtrl5)
|
||||
KeyCtrlRsqBracket = Key(termbox.KeyCtrlRsqBracket)
|
||||
KeyCtrl6 = Key(termbox.KeyCtrl6)
|
||||
KeyCtrl7 = Key(termbox.KeyCtrl7)
|
||||
KeyCtrlSlash = Key(termbox.KeyCtrlSlash)
|
||||
KeyCtrlUnderscore = Key(termbox.KeyCtrlUnderscore)
|
||||
KeySpace = Key(termbox.KeySpace)
|
||||
KeyBackspace2 = Key(termbox.KeyBackspace2)
|
||||
KeyCtrl8 = Key(termbox.KeyCtrl8)
|
||||
)
|
||||
KeyCtrlTilde = Key(tcell.KeyF64) // arbitrary assignment
|
||||
KeyCtrlSpace = Key(tcell.KeyCtrlSpace)
|
||||
KeyCtrlA = Key(tcell.KeyCtrlA)
|
||||
KeyCtrlB = Key(tcell.KeyCtrlB)
|
||||
KeyCtrlC = Key(tcell.KeyCtrlC)
|
||||
KeyCtrlD = Key(tcell.KeyCtrlD)
|
||||
KeyCtrlE = Key(tcell.KeyCtrlE)
|
||||
KeyCtrlF = Key(tcell.KeyCtrlF)
|
||||
KeyCtrlG = Key(tcell.KeyCtrlG)
|
||||
KeyBackspace = Key(tcell.KeyBackspace)
|
||||
KeyCtrlH = Key(tcell.KeyCtrlH)
|
||||
KeyTab = Key(tcell.KeyTab)
|
||||
KeyCtrlI = Key(tcell.KeyCtrlI)
|
||||
KeyCtrlJ = Key(tcell.KeyCtrlJ)
|
||||
KeyCtrlK = Key(tcell.KeyCtrlK)
|
||||
KeyCtrlL = Key(tcell.KeyCtrlL)
|
||||
KeyEnter = Key(tcell.KeyEnter)
|
||||
KeyCtrlM = Key(tcell.KeyCtrlM)
|
||||
KeyCtrlN = Key(tcell.KeyCtrlN)
|
||||
KeyCtrlO = Key(tcell.KeyCtrlO)
|
||||
KeyCtrlP = Key(tcell.KeyCtrlP)
|
||||
KeyCtrlQ = Key(tcell.KeyCtrlQ)
|
||||
KeyCtrlR = Key(tcell.KeyCtrlR)
|
||||
KeyCtrlS = Key(tcell.KeyCtrlS)
|
||||
KeyCtrlT = Key(tcell.KeyCtrlT)
|
||||
KeyCtrlU = Key(tcell.KeyCtrlU)
|
||||
KeyCtrlV = Key(tcell.KeyCtrlV)
|
||||
KeyCtrlW = Key(tcell.KeyCtrlW)
|
||||
KeyCtrlX = Key(tcell.KeyCtrlX)
|
||||
KeyCtrlY = Key(tcell.KeyCtrlY)
|
||||
KeyCtrlZ = Key(tcell.KeyCtrlZ)
|
||||
KeyEsc = Key(tcell.KeyEscape)
|
||||
KeyCtrlUnderscore = Key(tcell.KeyCtrlUnderscore)
|
||||
KeySpace = Key(32)
|
||||
KeyBackspace2 = Key(tcell.KeyBackspace2)
|
||||
KeyCtrl8 = Key(tcell.KeyBackspace2) // same key as in termbox-go
|
||||
|
||||
// Modifier allows to define special keys combinations. They can be used
|
||||
// in combination with Keys or Runes when a new keybinding is defined.
|
||||
type Modifier termbox.Modifier
|
||||
// The following assignments were used in termbox implementation.
|
||||
// In tcell, these are not keys per se. But in gocui we have them
|
||||
// mapped to the keys so we have to use placeholder keys.
|
||||
|
||||
MouseLeft = Key(tcell.KeyF63) // arbitrary assignments
|
||||
MouseRight = Key(tcell.KeyF62)
|
||||
MouseMiddle = Key(tcell.KeyF61)
|
||||
MouseRelease = Key(tcell.KeyF60)
|
||||
MouseWheelUp = Key(tcell.KeyF59)
|
||||
MouseWheelDown = Key(tcell.KeyF58)
|
||||
MouseWheelLeft = Key(tcell.KeyF57)
|
||||
MouseWheelRight = Key(tcell.KeyF56)
|
||||
KeyCtrl2 = Key(tcell.KeyNUL) // termbox defines theses
|
||||
KeyCtrl3 = Key(tcell.KeyEscape)
|
||||
KeyCtrl4 = Key(tcell.KeyCtrlBackslash)
|
||||
KeyCtrl5 = Key(tcell.KeyCtrlRightSq)
|
||||
KeyCtrl6 = Key(tcell.KeyCtrlCarat)
|
||||
KeyCtrl7 = Key(tcell.KeyCtrlUnderscore)
|
||||
KeyCtrlSlash = Key(tcell.KeyCtrlUnderscore)
|
||||
KeyCtrlRsqBracket = Key(tcell.KeyCtrlRightSq)
|
||||
KeyCtrlBackslash = Key(tcell.KeyCtrlBackslash)
|
||||
KeyCtrlLsqBracket = Key(tcell.KeyCtrlLeftSq)
|
||||
)
|
||||
|
||||
// Modifiers.
|
||||
const (
|
||||
ModNone Modifier = Modifier(0)
|
||||
ModAlt = Modifier(termbox.ModAlt)
|
||||
ModMotion = Modifier(termbox.ModMotion)
|
||||
ModNone Modifier = Modifier(0)
|
||||
ModAlt = Modifier(tcell.ModAlt)
|
||||
// ModCtrl doesn't work with keyboard keys. Use CtrlKey in Key and ModNone. This is was for mouse clicks only (tcell.v1)
|
||||
// ModCtrl = Modifier(tcell.ModCtrl)
|
||||
)
|
||||
|
46
vendor/github.com/jesseduffield/gocui/loader.go
generated
vendored
Normal file
46
vendor/github.com/jesseduffield/gocui/loader.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package gocui
|
||||
|
||||
import "time"
|
||||
|
||||
func (g *Gui) loaderTick() {
|
||||
go func() {
|
||||
for range time.Tick(time.Millisecond * 50) {
|
||||
for _, view := range g.Views() {
|
||||
if view.HasLoader {
|
||||
g.userEvents <- userEvent{func(g *Gui) error { return nil }}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (v *View) loaderLines() [][]cell {
|
||||
duplicate := make([][]cell, len(v.lines))
|
||||
for i := range v.lines {
|
||||
if i < len(v.lines)-1 {
|
||||
duplicate[i] = make([]cell, len(v.lines[i]))
|
||||
copy(duplicate[i], v.lines[i])
|
||||
} else {
|
||||
duplicate[i] = make([]cell, len(v.lines[i])+2)
|
||||
copy(duplicate[i], v.lines[i])
|
||||
duplicate[i][len(duplicate[i])-2] = cell{chr: ' '}
|
||||
duplicate[i][len(duplicate[i])-1] = Loader()
|
||||
}
|
||||
}
|
||||
|
||||
return duplicate
|
||||
}
|
||||
|
||||
// Loader can show a loading animation
|
||||
func Loader() cell {
|
||||
characters := "|/-\\"
|
||||
now := time.Now()
|
||||
nanos := now.UnixNano()
|
||||
index := nanos / 50000000 % int64(len(characters))
|
||||
str := characters[index : index+1]
|
||||
chr := []rune(str)[0]
|
||||
return cell{
|
||||
chr: chr,
|
||||
}
|
||||
}
|
205
vendor/github.com/jesseduffield/gocui/tcell_driver.go
generated
vendored
Normal file
205
vendor/github.com/jesseduffield/gocui/tcell_driver.go
generated
vendored
Normal file
@ -0,0 +1,205 @@
|
||||
// Copyright 2020 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 gocui
|
||||
|
||||
import (
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
var screen tcell.Screen
|
||||
|
||||
// tcellInit initializes tcell screen for use.
|
||||
func tcellInit() error {
|
||||
if s, e := tcell.NewScreen(); e != nil {
|
||||
return e
|
||||
} else if e = s.Init(); e != nil {
|
||||
return e
|
||||
} else {
|
||||
screen = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// tcellSetCell sets the character cell at a given location to the given
|
||||
// content (rune) and attributes using provided OutputMode
|
||||
func tcellSetCell(x, y int, ch rune, fg, bg Attribute, omode OutputMode) {
|
||||
st := getTcellStyle(fg, bg, omode)
|
||||
screen.SetContent(x, y, ch, nil, st)
|
||||
}
|
||||
|
||||
// getTcellStyle creates tcell.Style from Attributes
|
||||
func getTcellStyle(fg, bg Attribute, omode OutputMode) tcell.Style {
|
||||
st := tcell.StyleDefault
|
||||
|
||||
// extract colors and attributes
|
||||
if fg != ColorDefault {
|
||||
st = st.Foreground(getTcellColor(fg, omode))
|
||||
st = setTcellFontEffectStyle(st, fg)
|
||||
}
|
||||
if bg != ColorDefault {
|
||||
st = st.Background(getTcellColor(bg, omode))
|
||||
st = setTcellFontEffectStyle(st, bg)
|
||||
}
|
||||
|
||||
return st
|
||||
}
|
||||
|
||||
// setTcellFontEffectStyle add additional attributes to tcell.Style
|
||||
func setTcellFontEffectStyle(st tcell.Style, attr Attribute) tcell.Style {
|
||||
if attr&AttrBold != 0 {
|
||||
st = st.Bold(true)
|
||||
}
|
||||
if attr&AttrUnderline != 0 {
|
||||
st = st.Underline(true)
|
||||
}
|
||||
if attr&AttrReverse != 0 {
|
||||
st = st.Reverse(true)
|
||||
}
|
||||
if attr&AttrBlink != 0 {
|
||||
st = st.Blink(true)
|
||||
}
|
||||
if attr&AttrDim != 0 {
|
||||
st = st.Dim(true)
|
||||
}
|
||||
if attr&AttrItalic != 0 {
|
||||
st = st.Italic(true)
|
||||
}
|
||||
if attr&AttrStrikeThrough != 0 {
|
||||
st = st.StrikeThrough(true)
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
// gocuiEventType represents the type of event.
|
||||
type gocuiEventType uint8
|
||||
|
||||
// GocuiEvent represents events like a keys, mouse actions, or window resize.
|
||||
// The 'Mod', 'Key' and 'Ch' fields are valid if 'Type' is 'eventKey'.
|
||||
// The 'MouseX' and 'MouseY' fields are valid if 'Type' is 'eventMouse'.
|
||||
// The 'Width' and 'Height' fields are valid if 'Type' is 'eventResize'.
|
||||
// The 'Err' field is valid if 'Type' is 'eventError'.
|
||||
type GocuiEvent struct {
|
||||
Type gocuiEventType
|
||||
Mod Modifier
|
||||
Key Key
|
||||
Ch rune
|
||||
Width int
|
||||
Height int
|
||||
Err error
|
||||
MouseX int
|
||||
MouseY int
|
||||
N int
|
||||
}
|
||||
|
||||
// Event types.
|
||||
const (
|
||||
eventNone gocuiEventType = iota
|
||||
eventKey
|
||||
eventResize
|
||||
eventMouse
|
||||
eventInterrupt
|
||||
eventError
|
||||
eventRaw
|
||||
)
|
||||
|
||||
var (
|
||||
lastMouseKey tcell.ButtonMask = tcell.ButtonNone
|
||||
lastMouseMod tcell.ModMask = tcell.ModNone
|
||||
)
|
||||
|
||||
// pollEvent get tcell.Event and transform it into gocuiEvent
|
||||
func pollEvent() GocuiEvent {
|
||||
tev := screen.PollEvent()
|
||||
switch tev := tev.(type) {
|
||||
case *tcell.EventInterrupt:
|
||||
return GocuiEvent{Type: eventInterrupt}
|
||||
case *tcell.EventResize:
|
||||
w, h := tev.Size()
|
||||
return GocuiEvent{Type: eventResize, Width: w, Height: h}
|
||||
case *tcell.EventKey:
|
||||
k := tev.Key()
|
||||
ch := rune(0)
|
||||
if k == tcell.KeyRune {
|
||||
k = 0 // if rune remove key (so it can match rune instead of key)
|
||||
ch = tev.Rune()
|
||||
if ch == ' ' {
|
||||
// special handling for spacebar
|
||||
k = 32 // tcell keys ends at 31 or starts at 256
|
||||
ch = rune(0)
|
||||
}
|
||||
}
|
||||
mod := tev.Modifiers()
|
||||
// remove control modifier and setup special handling of ctrl+spacebar, etc.
|
||||
if mod == tcell.ModCtrl && k == 32 {
|
||||
mod = 0
|
||||
ch = rune(0)
|
||||
k = tcell.KeyCtrlSpace
|
||||
} else if mod == tcell.ModCtrl || mod == tcell.ModShift {
|
||||
// remove Ctrl or Shift if specified
|
||||
// - shift - will be translated to the final code of rune
|
||||
// - ctrl - is translated in the key
|
||||
mod = 0
|
||||
}
|
||||
return GocuiEvent{
|
||||
Type: eventKey,
|
||||
Key: Key(k),
|
||||
Ch: ch,
|
||||
Mod: Modifier(mod),
|
||||
}
|
||||
case *tcell.EventMouse:
|
||||
x, y := tev.Position()
|
||||
button := tev.Buttons()
|
||||
mouseKey := MouseRelease
|
||||
mouseMod := ModNone
|
||||
// process mouse wheel
|
||||
if button&tcell.WheelUp != 0 {
|
||||
mouseKey = MouseWheelUp
|
||||
}
|
||||
if button&tcell.WheelDown != 0 {
|
||||
mouseKey = MouseWheelDown
|
||||
}
|
||||
if button&tcell.WheelLeft != 0 {
|
||||
mouseKey = MouseWheelLeft
|
||||
}
|
||||
if button&tcell.WheelRight != 0 {
|
||||
mouseKey = MouseWheelRight
|
||||
}
|
||||
|
||||
// process button events (not wheel events)
|
||||
button &= tcell.ButtonMask(0xff)
|
||||
if button != tcell.ButtonNone && lastMouseKey == tcell.ButtonNone {
|
||||
lastMouseKey = button
|
||||
lastMouseMod = tev.Modifiers()
|
||||
}
|
||||
|
||||
switch tev.Buttons() {
|
||||
case tcell.ButtonNone:
|
||||
if lastMouseKey != tcell.ButtonNone {
|
||||
switch lastMouseKey {
|
||||
case tcell.ButtonPrimary:
|
||||
mouseKey = MouseLeft
|
||||
case tcell.ButtonSecondary:
|
||||
mouseKey = MouseRight
|
||||
case tcell.ButtonMiddle:
|
||||
mouseKey = MouseMiddle
|
||||
}
|
||||
mouseMod = Modifier(lastMouseMod)
|
||||
lastMouseMod = tcell.ModNone
|
||||
lastMouseKey = tcell.ButtonNone
|
||||
}
|
||||
}
|
||||
|
||||
return GocuiEvent{
|
||||
Type: eventMouse,
|
||||
MouseX: x,
|
||||
MouseY: y,
|
||||
Key: mouseKey,
|
||||
Ch: 0,
|
||||
Mod: mouseMod,
|
||||
}
|
||||
default:
|
||||
return GocuiEvent{Type: eventNone}
|
||||
}
|
||||
}
|
441
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
441
vendor/github.com/jesseduffield/gocui/view.go
generated
vendored
@ -9,12 +9,10 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
|
||||
"github.com/jesseduffield/termbox-go"
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
@ -26,21 +24,41 @@ const (
|
||||
RIGHT = 8 // view is overlapping at right edge
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidPoint is returned when client passed invalid coordinates of a cell.
|
||||
// Most likely client has passed negative coordinates of a cell.
|
||||
ErrInvalidPoint = errors.New("invalid point")
|
||||
)
|
||||
|
||||
// A View is a window. It maintains its own internal buffer and cursor
|
||||
// position.
|
||||
type View struct {
|
||||
name string
|
||||
x0, y0, x1, y1 int
|
||||
ox, oy int
|
||||
cx, cy int
|
||||
lines [][]cell
|
||||
readOffset int
|
||||
readCache string
|
||||
x0, y0, x1, y1 int // left top right bottom
|
||||
ox, oy int // view offsets
|
||||
cx, cy int // cursor position
|
||||
rx, ry int // Read() offsets
|
||||
wx, wy int // Write() offsets
|
||||
lines [][]cell // All the data
|
||||
outMode OutputMode
|
||||
|
||||
tainted bool // marks if the viewBuffer must be updated
|
||||
viewLines []viewLine // internal representation of the view's buffer
|
||||
// readBuffer is used for storing unread bytes
|
||||
readBuffer []byte
|
||||
|
||||
ei *escapeInterpreter // used to decode ESC sequences on Write
|
||||
// tained is true if the viewLines must be updated
|
||||
tainted bool
|
||||
|
||||
// internal representation of the view's buffer
|
||||
viewLines []viewLine
|
||||
|
||||
// writeMutex protects locks the write process
|
||||
writeMutex sync.Mutex
|
||||
|
||||
// ei is used to decode ESC sequences on Write
|
||||
ei *escapeInterpreter
|
||||
|
||||
// Visible specifies whether the view is visible.
|
||||
Visible bool
|
||||
|
||||
// BgColor and FgColor allow to configure the background and foreground
|
||||
// colors of the View.
|
||||
@ -54,7 +72,7 @@ type View struct {
|
||||
// buffer at the cursor position.
|
||||
Editable bool
|
||||
|
||||
// Editor allows to define the editor that manages the edition mode,
|
||||
// Editor allows to define the editor that manages the editing mode,
|
||||
// including keybindings or cursor behaviour. DefaultEditor is used by
|
||||
// default.
|
||||
Editor Editor
|
||||
@ -69,6 +87,24 @@ type View struct {
|
||||
// If Frame is true, a border will be drawn around the view.
|
||||
Frame bool
|
||||
|
||||
// FrameColor allow to configure the color of the Frame when it is not highlighted.
|
||||
FrameColor Attribute
|
||||
|
||||
// FrameRunes allows to define custom runes for the frame edges.
|
||||
// The rune slice can be defined with 3 different lengths.
|
||||
// If slice doesn't match these lengths, default runes will be used instead of missing one.
|
||||
//
|
||||
// 2 runes with only horizontal and vertical edges.
|
||||
// []rune{'─', '│'}
|
||||
// []rune{'═','║'}
|
||||
// 6 runes with horizontal, vertical edges and top-left, top-right, bottom-left, bottom-right cornes.
|
||||
// []rune{'─', '│', '┌', '┐', '└', '┘'}
|
||||
// []rune{'═','║','╔','╗','╚','╝'}
|
||||
// 11 runes which can be used with `gocui.Gui.SupportOverlaps` property.
|
||||
// []rune{'─', '│', '┌', '┐', '└', '┘', '├', '┤', '┬', '┴', '┼'}
|
||||
// []rune{'═','║','╔','╗','╚','╝','╠','╣','╦','╩','╬'}
|
||||
FrameRunes []rune
|
||||
|
||||
// If Wrap is true, the content that is written to this View is
|
||||
// automatically wrapped when it is longer than its width. If true the
|
||||
// view's x-origin will be ignored.
|
||||
@ -85,6 +121,8 @@ type View struct {
|
||||
TabIndex int
|
||||
// HighlightTabWithoutFocus allows you to show which tab is selected without the view being focused
|
||||
HighlightSelectedTabWithoutFocus bool
|
||||
// TitleColor allow to configure the color of title and subtitle for the view.
|
||||
TitleColor Attribute
|
||||
|
||||
// If Frame is true, Subtitle allows to configure a subtitle for the view.
|
||||
Subtitle string
|
||||
@ -99,8 +137,6 @@ type View struct {
|
||||
// If HasLoader is true, the message will be appended with a spinning loader animation
|
||||
HasLoader bool
|
||||
|
||||
writeMutex sync.Mutex
|
||||
|
||||
// IgnoreCarriageReturns tells us whether to ignore '\r' characters
|
||||
IgnoreCarriageReturns bool
|
||||
|
||||
@ -113,6 +149,10 @@ type View struct {
|
||||
|
||||
// when ContainsList is true, we show the current index and total count in the view
|
||||
ContainsList bool
|
||||
|
||||
// KeybindOnEdit should be set to true when you want to execute keybindings even when the view is editable
|
||||
// (this is usually not the case)
|
||||
KeybindOnEdit bool
|
||||
}
|
||||
|
||||
type searcher struct {
|
||||
@ -280,12 +320,18 @@ func newView(name string, x0, y0, x1, y1 int, mode OutputMode) *View {
|
||||
y0: y0,
|
||||
x1: x1,
|
||||
y1: y1,
|
||||
Visible: true,
|
||||
Frame: true,
|
||||
Editor: DefaultEditor,
|
||||
tainted: true,
|
||||
outMode: mode,
|
||||
ei: newEscapeInterpreter(mode),
|
||||
searcher: &searcher{},
|
||||
}
|
||||
|
||||
v.FgColor, v.BgColor = ColorDefault, ColorDefault
|
||||
v.SelFgColor, v.SelBgColor = ColorDefault, ColorDefault
|
||||
v.TitleColor, v.FrameColor = ColorDefault, ColorDefault
|
||||
return v
|
||||
}
|
||||
|
||||
@ -310,7 +356,7 @@ func (v *View) Name() string {
|
||||
func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
|
||||
maxX, maxY := v.Size()
|
||||
if x < 0 || x >= maxX || y < 0 || y >= maxY {
|
||||
return errors.New("invalid point")
|
||||
return ErrInvalidPoint
|
||||
}
|
||||
var (
|
||||
ry, rcy int
|
||||
@ -336,8 +382,12 @@ func (v *View) setRune(x, y int, ch rune, fgColor, bgColor Attribute) error {
|
||||
bgColor = bgColor | v.SelBgColor
|
||||
}
|
||||
|
||||
termbox.SetCell(v.x0+x+1, v.y0+y+1, ch,
|
||||
termbox.Attribute(fgColor), termbox.Attribute(bgColor))
|
||||
// Don't display NUL characters
|
||||
if ch == 0 {
|
||||
ch = ' '
|
||||
}
|
||||
|
||||
tcellSetCell(v.x0+x+1, v.y0+y+1, ch, fgColor, bgColor, v.outMode)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -365,6 +415,9 @@ func (v *View) Cursor() (x, y int) {
|
||||
// implement Horizontal and Vertical scrolling with just incrementing
|
||||
// or decrementing ox and oy.
|
||||
func (v *View) SetOrigin(x, y int) error {
|
||||
if x < 0 || y < 0 {
|
||||
return ErrInvalidPoint
|
||||
}
|
||||
v.ox = x
|
||||
v.oy = y
|
||||
return nil
|
||||
@ -375,6 +428,91 @@ func (v *View) Origin() (x, y int) {
|
||||
return v.ox, v.oy
|
||||
}
|
||||
|
||||
// SetWritePos sets the write position of the view's internal buffer.
|
||||
// So the next Write call would write directly to the specified position.
|
||||
func (v *View) SetWritePos(x, y int) error {
|
||||
if x < 0 || y < 0 {
|
||||
return ErrInvalidPoint
|
||||
}
|
||||
v.wx = x
|
||||
v.wy = y
|
||||
return nil
|
||||
}
|
||||
|
||||
// WritePos returns the current write position of the view's internal buffer.
|
||||
func (v *View) WritePos() (x, y int) {
|
||||
return v.wx, v.wy
|
||||
}
|
||||
|
||||
// SetReadPos sets the read position of the view's internal buffer.
|
||||
// So the next Read call would read from the specified position.
|
||||
func (v *View) SetReadPos(x, y int) error {
|
||||
if x < 0 || y < 0 {
|
||||
return ErrInvalidPoint
|
||||
}
|
||||
v.readBuffer = nil
|
||||
v.rx = x
|
||||
v.ry = y
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPos returns the current read position of the view's internal buffer.
|
||||
func (v *View) ReadPos() (x, y int) {
|
||||
return v.rx, v.ry
|
||||
}
|
||||
|
||||
// makeWriteable creates empty cells if required to make position (x, y) writeable.
|
||||
func (v *View) makeWriteable(x, y int) {
|
||||
// TODO: make this more efficient
|
||||
|
||||
// line `y` must be index-able (that's why `<=`)
|
||||
for len(v.lines) <= y {
|
||||
if cap(v.lines) > len(v.lines) {
|
||||
newLen := cap(v.lines)
|
||||
if newLen > y {
|
||||
newLen = y + 1
|
||||
}
|
||||
v.lines = v.lines[:newLen]
|
||||
} else {
|
||||
v.lines = append(v.lines, nil)
|
||||
}
|
||||
}
|
||||
// cell `x` must not be index-able (that's why `<`)
|
||||
// append should be used by `lines[y]` user if he wants to write beyond `x`
|
||||
for len(v.lines[y]) < x {
|
||||
if cap(v.lines[y]) > len(v.lines[y]) {
|
||||
newLen := cap(v.lines[y])
|
||||
if newLen > x {
|
||||
newLen = x
|
||||
}
|
||||
v.lines[y] = v.lines[y][:newLen]
|
||||
} else {
|
||||
v.lines[y] = append(v.lines[y], cell{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// writeCells copies []cell to specified location (x, y)
|
||||
// !!! caller MUST ensure that specified location (x, y) is writeable by calling makeWriteable
|
||||
func (v *View) writeCells(x, y int, cells []cell) {
|
||||
var newLen int
|
||||
// use maximum len available
|
||||
line := v.lines[y][:cap(v.lines[y])]
|
||||
maxCopy := len(line) - x
|
||||
if maxCopy < len(cells) {
|
||||
copy(line[x:], cells[:maxCopy])
|
||||
line = append(line, cells[maxCopy:]...)
|
||||
newLen = len(line)
|
||||
} else { // maxCopy >= len(cells)
|
||||
copy(line[x:], cells)
|
||||
newLen = x + len(cells)
|
||||
if newLen < len(v.lines[y]) {
|
||||
newLen = len(v.lines[y])
|
||||
}
|
||||
}
|
||||
v.lines[y] = line[:newLen]
|
||||
}
|
||||
|
||||
// Write appends a byte slice into the view's internal buffer. Because
|
||||
// View implements the io.Writer interface, it can be passed as parameter
|
||||
// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
|
||||
@ -382,71 +520,48 @@ func (v *View) Origin() (x, y int) {
|
||||
func (v *View) Write(p []byte) (n int, err error) {
|
||||
v.tainted = true
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
for _, ch := range bytes.Runes(p) {
|
||||
switch ch {
|
||||
case '\n':
|
||||
v.lines = append(v.lines, nil)
|
||||
case '\r':
|
||||
if v.IgnoreCarriageReturns {
|
||||
continue
|
||||
}
|
||||
nl := len(v.lines)
|
||||
if nl > 0 {
|
||||
v.lines[nl-1] = nil
|
||||
} else {
|
||||
v.lines = make([][]cell, 1)
|
||||
}
|
||||
default:
|
||||
cells := v.parseInput(ch)
|
||||
if v.ei.instruction.kind != NONE {
|
||||
switch v.ei.instruction.kind {
|
||||
case ERASE_IN_LINE:
|
||||
v.eraseInLine()
|
||||
}
|
||||
v.ei.instructionRead()
|
||||
continue
|
||||
}
|
||||
|
||||
if cells == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
nl := len(v.lines)
|
||||
if nl > 0 {
|
||||
v.lines[nl-1] = append(v.lines[nl-1], cells...)
|
||||
} else {
|
||||
v.lines = append(v.lines, cells)
|
||||
}
|
||||
}
|
||||
}
|
||||
v.makeWriteable(v.wx, v.wy)
|
||||
v.writeRunes(bytes.Runes(p))
|
||||
v.writeMutex.Unlock()
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (v *View) eraseInLine() {
|
||||
code := v.ei.instruction.param1
|
||||
switch code {
|
||||
case 0:
|
||||
// need to write till end of the line with cells containing the same bg colour as we currently have.
|
||||
func (v *View) WriteRunes(p []rune) {
|
||||
v.tainted = true
|
||||
|
||||
if len(v.lines) == 0 {
|
||||
v.lines = append(v.lines, []cell{})
|
||||
// Fill with empty cells, if writing outside current view buffer
|
||||
v.makeWriteable(v.wx, v.wy)
|
||||
v.writeRunes(p)
|
||||
}
|
||||
|
||||
func (v *View) WriteString(s string) {
|
||||
v.WriteRunes([]rune(s))
|
||||
}
|
||||
|
||||
// writeRunes copies slice of runes into internal lines buffer.
|
||||
// caller must make sure that writing position is accessable.
|
||||
func (v *View) writeRunes(p []rune) {
|
||||
for _, r := range p {
|
||||
switch r {
|
||||
case '\n':
|
||||
v.wy++
|
||||
if v.wy >= len(v.lines) {
|
||||
v.lines = append(v.lines, nil)
|
||||
}
|
||||
|
||||
fallthrough
|
||||
// not valid in every OS, but making runtime OS checks in cycle is bad.
|
||||
case '\r':
|
||||
v.wx = 0
|
||||
default:
|
||||
cells := v.parseInput(r)
|
||||
if cells == nil {
|
||||
continue
|
||||
}
|
||||
v.writeCells(v.wx, v.wy, cells)
|
||||
v.wx += len(cells)
|
||||
}
|
||||
nl := len(v.lines)
|
||||
width, _ := v.Size()
|
||||
cellCount := width - len(v.lines[nl-1])
|
||||
c := cell{
|
||||
fgColor: v.ei.curFgColor,
|
||||
bgColor: v.ei.curBgColor,
|
||||
chr: ' ',
|
||||
}
|
||||
for i := 0; i < cellCount; i++ {
|
||||
v.lines[nl-1] = append(v.lines[nl-1], c)
|
||||
}
|
||||
default:
|
||||
// don't recognise sequence. Until we merge the gocui master branch we can't handle going backwards.
|
||||
}
|
||||
}
|
||||
|
||||
@ -489,26 +604,54 @@ func (v *View) parseInput(ch rune) []cell {
|
||||
return cells
|
||||
}
|
||||
|
||||
// Read reads data into p. It returns the number of bytes read into p.
|
||||
// At EOF, err will be io.EOF. Calling Read() after Rewind() makes the
|
||||
// cache to be refreshed with the contents of the view.
|
||||
// Read reads data into p from the current reading position set by SetReadPos.
|
||||
// It returns the number of bytes read into p.
|
||||
// At EOF, err will be io.EOF.
|
||||
func (v *View) Read(p []byte) (n int, err error) {
|
||||
if v.readOffset == 0 {
|
||||
v.readCache = v.Buffer()
|
||||
buffer := make([]byte, utf8.UTFMax)
|
||||
offset := 0
|
||||
if v.readBuffer != nil {
|
||||
copy(p, v.readBuffer)
|
||||
if len(v.readBuffer) >= len(p) {
|
||||
if len(v.readBuffer) > len(p) {
|
||||
v.readBuffer = v.readBuffer[len(p):]
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
v.readBuffer = nil
|
||||
}
|
||||
if v.readOffset < len(v.readCache) {
|
||||
n = copy(p, v.readCache[v.readOffset:])
|
||||
v.readOffset += n
|
||||
} else {
|
||||
err = io.EOF
|
||||
for v.ry < len(v.lines) {
|
||||
for v.rx < len(v.lines[v.ry]) {
|
||||
count := utf8.EncodeRune(buffer, v.lines[v.ry][v.rx].chr)
|
||||
copy(p[offset:], buffer[:count])
|
||||
v.rx++
|
||||
newOffset := offset + count
|
||||
if newOffset >= len(p) {
|
||||
if newOffset > len(p) {
|
||||
v.readBuffer = buffer[newOffset-len(p):]
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
offset += count
|
||||
}
|
||||
v.rx = 0
|
||||
v.ry++
|
||||
}
|
||||
return
|
||||
return offset, io.EOF
|
||||
}
|
||||
|
||||
// Rewind sets the offset for the next Read to 0, which also refresh the
|
||||
// read cache.
|
||||
// Rewind sets read and write pos to (0, 0).
|
||||
func (v *View) Rewind() {
|
||||
v.readOffset = 0
|
||||
if err := v.SetReadPos(0, 0); err != nil {
|
||||
// SetReadPos returns error only if x and y are negative
|
||||
// we are passing 0, 0, thus no error should occur.
|
||||
panic(err)
|
||||
}
|
||||
if err := v.SetWritePos(0, 0); err != nil {
|
||||
// SetWritePos returns error only if x and y are negative
|
||||
// we are passing 0, 0, thus no error should occur.
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func containsUpcaseChar(str string) bool {
|
||||
@ -517,6 +660,7 @@ func containsUpcaseChar(str string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@ -551,13 +695,18 @@ func (v *View) updateSearchPositions() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsTainted tells us if the view is tainted
|
||||
func (v *View) IsTainted() bool {
|
||||
return v.tainted
|
||||
}
|
||||
|
||||
// draw re-draws the view's contents.
|
||||
func (v *View) draw() error {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
if !v.Visible {
|
||||
return nil
|
||||
}
|
||||
|
||||
v.updateSearchPositions()
|
||||
maxX, maxY := v.Size()
|
||||
@ -630,7 +779,15 @@ func (v *View) draw() error {
|
||||
if err := v.setRune(x, y, c.chr, fgColor, bgColor); err != nil {
|
||||
return err
|
||||
}
|
||||
x += runewidth.RuneWidth(c.chr)
|
||||
|
||||
if c.chr != 0 {
|
||||
// If it is a rune, add rune width
|
||||
x += runewidth.RuneWidth(c.chr)
|
||||
} else {
|
||||
// If it is NULL rune, add 1 to be able to use SetWritePos
|
||||
// (runewidth.RuneWidth of space is 1)
|
||||
x++
|
||||
}
|
||||
}
|
||||
y++
|
||||
}
|
||||
@ -656,7 +813,7 @@ func (v *View) realPosition(vx, vy int) (x, y int, err error) {
|
||||
vy = v.oy + vy
|
||||
|
||||
if vx < 0 || vy < 0 {
|
||||
return 0, 0, errors.New("invalid point")
|
||||
return 0, 0, ErrInvalidPoint
|
||||
}
|
||||
|
||||
if len(v.viewLines) == 0 {
|
||||
@ -677,17 +834,16 @@ func (v *View) realPosition(vx, vy int) (x, y int, err error) {
|
||||
}
|
||||
|
||||
// Clear empties the view's internal buffer.
|
||||
// And resets reading and writing offsets.
|
||||
func (v *View) Clear() {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
|
||||
v.Rewind()
|
||||
v.tainted = true
|
||||
v.ei.reset()
|
||||
|
||||
v.lines = nil
|
||||
v.viewLines = nil
|
||||
v.readOffset = 0
|
||||
v.clearRunes()
|
||||
v.writeMutex.Unlock()
|
||||
}
|
||||
|
||||
// clearRunes erases all the cells in the view.
|
||||
@ -695,8 +851,7 @@ func (v *View) clearRunes() {
|
||||
maxX, maxY := v.Size()
|
||||
for x := 0; x < maxX; x++ {
|
||||
for y := 0; y < maxY; y++ {
|
||||
termbox.SetCell(v.x0+x+1, v.y0+y+1, ' ',
|
||||
termbox.Attribute(v.FgColor), termbox.Attribute(v.BgColor))
|
||||
tcellSetCell(v.x0+x+1, v.y0+y+1, ' ', v.FgColor, v.BgColor, v.outMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -704,8 +859,6 @@ func (v *View) clearRunes() {
|
||||
// BufferLines returns the lines in the view's internal
|
||||
// buffer.
|
||||
func (v *View) BufferLines() []string {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
lines := make([]string, len(v.lines))
|
||||
for i, l := range v.lines {
|
||||
str := lineType(l).String()
|
||||
@ -724,8 +877,6 @@ func (v *View) Buffer() string {
|
||||
// ViewBufferLines returns the lines in the view's internal
|
||||
// buffer that is shown to the user.
|
||||
func (v *View) ViewBufferLines() []string {
|
||||
v.writeMutex.Lock()
|
||||
defer v.writeMutex.Unlock()
|
||||
lines := make([]string, len(v.viewLines))
|
||||
for i, l := range v.viewLines {
|
||||
str := lineType(l.line).String()
|
||||
@ -765,7 +916,7 @@ func (v *View) Line(y int) (string, error) {
|
||||
}
|
||||
|
||||
if y < 0 || y >= len(v.lines) {
|
||||
return "", errors.New("invalid point")
|
||||
return "", ErrInvalidPoint
|
||||
}
|
||||
|
||||
return lineType(v.lines[y]).String(), nil
|
||||
@ -780,7 +931,7 @@ func (v *View) Word(x, y int) (string, error) {
|
||||
}
|
||||
|
||||
if x < 0 || y < 0 || y >= len(v.lines) || x >= len(v.lines[y]) {
|
||||
return "", errors.New("invalid point")
|
||||
return "", ErrInvalidPoint
|
||||
}
|
||||
|
||||
str := lineType(v.lines[y]).String()
|
||||
@ -806,6 +957,48 @@ func indexFunc(r rune) bool {
|
||||
return r == ' ' || r == 0
|
||||
}
|
||||
|
||||
// SetLine changes the contents of an existing line.
|
||||
func (v *View) SetLine(y int, text string) error {
|
||||
if y < 0 || y >= len(v.lines) {
|
||||
err := ErrInvalidPoint
|
||||
return err
|
||||
}
|
||||
|
||||
v.tainted = true
|
||||
line := make([]cell, 0)
|
||||
for _, r := range text {
|
||||
c := v.parseInput(r)
|
||||
line = append(line, c...)
|
||||
}
|
||||
v.lines[y] = line
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHighlight toggles highlighting of separate lines, for custom lists
|
||||
// or multiple selection in views.
|
||||
func (v *View) SetHighlight(y int, on bool) error {
|
||||
if y < 0 || y >= len(v.lines) {
|
||||
err := ErrInvalidPoint
|
||||
return err
|
||||
}
|
||||
|
||||
line := v.lines[y]
|
||||
cells := make([]cell, 0)
|
||||
for _, c := range line {
|
||||
if on {
|
||||
c.bgColor = v.SelBgColor
|
||||
c.fgColor = v.SelFgColor
|
||||
} else {
|
||||
c.bgColor = v.BgColor
|
||||
c.fgColor = v.FgColor
|
||||
}
|
||||
cells = append(cells, c)
|
||||
}
|
||||
v.tainted = true
|
||||
v.lines[y] = cells
|
||||
return nil
|
||||
}
|
||||
|
||||
func lineWidth(line []cell) (n int) {
|
||||
for i := range line {
|
||||
n += runewidth.RuneWidth(line[i].chr)
|
||||
@ -852,40 +1045,6 @@ func linesToString(lines [][]cell) string {
|
||||
return strings.Join(str, "\n")
|
||||
}
|
||||
|
||||
func (v *View) loaderLines() [][]cell {
|
||||
duplicate := make([][]cell, len(v.lines))
|
||||
for i := range v.lines {
|
||||
if i < len(v.lines)-1 {
|
||||
duplicate[i] = make([]cell, len(v.lines[i]))
|
||||
copy(duplicate[i], v.lines[i])
|
||||
} else {
|
||||
duplicate[i] = make([]cell, len(v.lines[i])+2)
|
||||
copy(duplicate[i], v.lines[i])
|
||||
duplicate[i][len(duplicate[i])-2] = cell{chr: ' '}
|
||||
duplicate[i][len(duplicate[i])-1] = Loader()
|
||||
}
|
||||
}
|
||||
|
||||
return duplicate
|
||||
}
|
||||
|
||||
func Loader() cell {
|
||||
characters := "|/-\\"
|
||||
now := time.Now()
|
||||
nanos := now.UnixNano()
|
||||
index := nanos / 50000000 % int64(len(characters))
|
||||
str := characters[index : index+1]
|
||||
chr := []rune(str)[0]
|
||||
return cell{
|
||||
chr: chr,
|
||||
}
|
||||
}
|
||||
|
||||
// IsTainted tells us if the view is tainted
|
||||
func (v *View) IsTainted() bool {
|
||||
return v.tainted
|
||||
}
|
||||
|
||||
// GetClickedTabIndex tells us which tab was clicked
|
||||
func (v *View) GetClickedTabIndex(x int) int {
|
||||
if len(v.Tabs) <= 1 {
|
||||
|
Reference in New Issue
Block a user