summaryrefslogtreecommitdiff
path: root/examples/go-dashboard/src/github.com/mum4k/termdash
diff options
context:
space:
mode:
Diffstat (limited to 'examples/go-dashboard/src/github.com/mum4k/termdash')
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/.gitignore2
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/.travis.yml17
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/CHANGELOG.md361
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/CONTRIBUTING.md38
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/LICENSE201
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/README.md215
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/align/align.go70
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/cell/cell.go64
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/cell/color.go106
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/container/container.go471
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/container/draw.go175
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/container/focus.go116
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/container/options.go817
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/container/traversal.go86
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/go.mod10
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/keyboard/keyboard.go172
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/linestyle/linestyle.go51
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/mouse/mouse.go48
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/alignfor/alignfor.go128
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/area/area.go258
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/button/button.go135
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/braille/braille.go284
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/buffer/buffer.go188
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/canvas.go247
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/border.go182
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_circle.go263
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_fill.go160
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_line.go204
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/draw.go17
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line.go207
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line_graph.go206
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/line_style.go129
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/rectangle.go93
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/text.go195
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/vertical_text.go120
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/event/event.go260
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/event/eventqueue/eventqueue.go231
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/numbers.go222
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/trig/trig.go224
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/runewidth/runewidth.go98
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/private/wrap/wrap.go409
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/termdash.go362
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/cell_options.go37
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/color_mode.go38
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/event.go179
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/termbox.go164
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/color_mode.go60
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/event.go106
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/terminalapi.go56
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/widgetapi/widgetapi.go185
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/line_trim.go117
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/options.go156
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/scroll.go165
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/text.go286
-rw-r--r--examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/write_options.go67
55 files changed, 0 insertions, 9458 deletions
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/.gitignore b/examples/go-dashboard/src/github.com/mum4k/termdash/.gitignore
deleted file mode 100644
index 97e9bcbaa..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Exclude MacOS attribute files.
-.DS_Store
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/.travis.yml b/examples/go-dashboard/src/github.com/mum4k/termdash/.travis.yml
deleted file mode 100644
index 7c8b739a0..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/.travis.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-language: go
-go:
- - 1.14.x
- - 1.15.x
- - stable
-script:
- - go get -t ./...
- - go get -u golang.org/x/lint/golint
- - go test ./...
- - CGO_ENABLED=1 go test -race ./...
- - go vet ./...
- - diff -u <(echo -n) <(gofmt -d -s .)
- - diff -u <(echo -n) <(./internal/scripts/autogen_licences.sh .)
- - diff -u <(echo -n) <(golint ./...)
-env:
- global:
- - CGO_ENABLED=0
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/CHANGELOG.md b/examples/go-dashboard/src/github.com/mum4k/termdash/CHANGELOG.md
deleted file mode 100644
index 6889100b4..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/CHANGELOG.md
+++ /dev/null
@@ -1,361 +0,0 @@
-# Changelog
-
-All notable changes to this project are documented here.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-
-## [Unreleased]
-
-## [0.12.2] - 31-Aug-2020
-
-### Fixed
-
-- advanced the CI Go versions up to Go 1.15.
-- fixed the build status badge to correctly point to travis-ci.com instead of
- travis-ci.org.
-
-## [0.12.1] - 20-Jun-2020
-
-### Fixed
-
-- the `tcell` unit test can now pass in headless mode (when TERM="") which
- happens under bazel.
-- switching coveralls integration to Github application.
-
-## [0.12.0] - 10-Apr-2020
-
-### Added
-
-- Migrating to [Go modules](https://blog.golang.org/using-go-modules).
-- Renamed directory `internal` to `private` so that external widget development
- is possible. Noted in
- [README.md](https://github.com/mum4k/termdash/blob/master/README.md) that packages in the
- `private` directory don't have any API stability guarantee.
-
-## [0.11.0] - 7-Mar-2020
-
-#### Breaking API changes
-
-- Termdash now requires at least Go version 1.11.
-
-### Added
-
-- New [`tcell`](https://github.com/gdamore/tcell) based terminal implementation
- which implements the `terminalapi.Terminal` interface.
-- tcell implementation supports two initialization `Option`s:
- - `ColorMode` the terminal color output mode (defaults to 256 color mode)
- - `ClearStyle` the foreground and background color style to use when clearing
- the screen (defaults to the global ColorDefault for both foreground and
- background)
-
-### Fixed
-
-- Improved test coverage of the `Gauge` widget.
-
-## [0.10.0] - 5-Jun-2019
-
-### Added
-
-- Added `time.Duration` based `ValueFormatter` for the `LineChart` Y-axis labels.
-- Added round and suffix `ValueFormatter` for the `LineChart` Y-axis labels.
-- Added decimal and suffix `ValueFormatter` for the `LineChart` Y-axis labels.
-- Added a `container.SplitOption` that allows fixed size container splits.
-- Added `grid` functions that allow fixed size rows and columns.
-
-### Changed
-
-- The `LineChart` can format the labels on the Y-axis with a `ValueFormatter`.
-- The `SegmentDisplay` can now display dots and colons ('.' and ':').
-- The `Donut` widget now guarantees spacing between the donut and its label.
-- The continuous build on Travis CI now builds with cgo explicitly disabled to
- ensure both Termdash and its dependencies use pure Go.
-
-### Fixed
-
-- Lint issues found on the Go report card.
-- An internal library belonging to the `Text` widget was incorrectly passing
- `math.MaxUint32` as an int argument.
-
-## [0.9.1] - 15-May-2019
-
-### Fixed
-
-- Termdash could deadlock when a `Button` or a `TextInput` was configured to
- call the `Container.Update` method.
-
-## [0.9.0] - 28-Apr-2019
-
-### Added
-
-- The `TextInput` widget, an input field allowing interactive text input.
-- The `Donut` widget can now display an optional text label under the donut.
-
-### Changed
-
-- Widgets now get information whether their container is focused when Draw is
- executed.
-- The SegmentDisplay widget now has a method that returns the observed character
- capacity the last time Draw was called.
-- The grid.Builder API now allows users to specify options for intermediate
- containers, i.e. containers that don't have widgets, but represent rows and
- columns.
-- Line chart widget now allows `math.NaN` values to represent "no value" (values
- that will not be rendered) in the values slice.
-
-#### Breaking API changes
-
-- The widgetapi.Widget.Draw method now accepts a second argument which provides
- widgets with additional metadata. This affects all implemented widgets.
-- Termdash now requires at least Go version 1.10, which allows us to utilize
- `math.Round` instead of our own implementation and `strings.Builder` instead
- of `bytes.Buffer`.
-- Terminal shortcuts like `Ctrl-A` no longer come as two separate events,
- Termdash now mirrors termbox-go and sends these as one event.
-
-## [0.8.0] - 30-Mar-2019
-
-### Added
-
-- New API for building layouts, a grid.Builder. Allows defining the layout
- iteratively as repetitive Elements, Rows and Columns.
-- Containers now support margin around them and padding of their content.
-- Container now supports dynamic layout changes via the new Update method.
-
-### Changed
-
-- The Text widget now supports content wrapping on word boundaries.
-- The BarChart and SparkLine widgets now have a method that returns the
- observed value capacity the last time Draw was called.
-- Moving widgetapi out of the internal directory to allow external users to
- develop their own widgets.
-- Event delivery to widgets now has a stable defined order and happens when the
- container is unlocked so that widgets can trigger dynamic layout changes.
-
-### Fixed
-
-- The termdash_test now correctly waits until all subscribers processed events,
- not just received them.
-- Container focus tracker now correctly tracks focus changes in enlarged areas,
- i.e. when the terminal size increased.
-- The BarChart, LineChart and SegmentDisplay widgets now protect against
- external mutation of the values passed into them by copying the data they
- receive.
-
-## [0.7.2] - 25-Feb-2019
-
-### Added
-
-- Test coverage for data only packages.
-
-### Changed
-
-- Refactoring packages that contained a mix of public and internal identifiers.
-
-#### Breaking API changes
-
-The following packages were refactored, no impact is expected as the removed
-identifiers shouldn't be used externally.
-
-- Functions align.Text and align.Rectangle were moved to a new
- internal/alignfor package.
-- Types cell.Cell and cell.Buffer were moved into a new internal/canvas/buffer
- package.
-
-## [0.7.1] - 24-Feb-2019
-
-### Fixed
-
-- Some of the packages that were moved into internal are required externally.
- This release makes them available again.
-
-### Changed
-
-#### Breaking API changes
-
-- The draw.LineStyle enum was refactored into its own package
- linestyle.LineStyle. Users will have to replace:
-
- - draw.LineStyleNone -> linestyle.None
- - draw.LineStyleLight -> linestyle.Light
- - draw.LineStyleDouble -> linestyle.Double
- - draw.LineStyleRound -> linestyle.Round
-
-## [0.7.0] - 24-Feb-2019
-
-### Added
-
-#### New widgets
-
-- The Button widget.
-
-#### Improvements to documentation
-
-- Clearly marked the public API surface by moving private packages into
- internal directory.
-- Started a GitHub wiki for Termdash.
-
-#### Improvements to the LineChart widget
-
-- The LineChart widget can display X axis labels in vertical orientation.
-- The LineChart widget allows the user to specify a custom scale for the Y
- axis.
-- The LineChart widget now has an option that disables scaling of the X axis.
- Useful for applications that want to continuously feed data and make them
- "roll" through the linechart.
-- The LineChart widget now has a method that returns the observed capacity of
- the LineChart the last time Draw was called.
-- The LineChart widget now supports zoom of the content triggered by mouse
- events.
-
-#### Improvements to the Text widget
-
-- The Text widget now has a Write option that atomically replaces the entire
- text content.
-
-#### Improvements to the infrastructure
-
-- A function that draws text vertically.
-- A non-blocking event distribution system that can throttle repetitive events.
-- Generalized mouse button FSM for use in widgets that need to track mouse
- button clicks.
-
-### Changed
-
-- Termbox is now initialized in 256 color mode by default.
-- The infrastructure now uses the non-blocking event distribution system to
- distribute events to subscribers. Each widget is now an individual
- subscriber.
-- The infrastructure now throttles event driven screen redraw rather than
- redrawing for each input event.
-- Widgets can now specify the scope at which they want to receive keyboard and
- mouse events.
-
-#### Breaking API changes
-
-##### High impact
-
-- The constructors of all the widgets now also return an error so that they
- can validate the options. This is a breaking change for the following
- widgets: BarChart, Gauge, LineChart, SparkLine, Text. The callers will have
- to handle the returned error.
-
-##### Low impact
-
-- The container package no longer exports separate methods to receive Keyboard
- and Mouse events which were replaced by a Subscribe method for the event
- distribution system. This shouldn't affect users as the removed methods
- aren't needed by container users.
-- The widgetapi.Options struct now uses an enum instead of a boolean when
- widget specifies if it wants keyboard or mouse events. This only impacts
- development of new widgets.
-
-### Fixed
-
-- The LineChart widget now correctly determines the Y axis scale when multiple
- series are provided.
-- Lint issues in the codebase, and updated Travis configuration so that golint
- is executed on every run.
-- Termdash now correctly starts in locales like zh_CN.UTF-8 where some of the
- characters it uses internally can have ambiguous width.
-
-## [0.6.1] - 12-Feb-2019
-
-### Fixed
-
-- The LineChart widget now correctly places custom labels.
-
-## [0.6.0] - 07-Feb-2019
-
-### Added
-
-- The SegmentDisplay widget.
-- A CHANGELOG.
-- New line styles for borders.
-
-### Changed
-
-- Better recordings of the individual demos.
-
-### Fixed
-
-- The LineChart now has an option to change the behavior of the Y axis from
- zero anchored to adaptive.
-- Lint errors reported on the Go report card.
-- Widgets now correctly handle a race when new user data are supplied between
- calls to their Options() and Draw() methods.
-
-## [0.5.0] - 21-Jan-2019
-
-### Added
-
-- Draw primitives for drawing circles.
-- The Donut widget.
-
-### Fixed
-
-- Bugfixes in the braille canvas.
-- Lint errors reported on the Go report card.
-- Flaky behavior in termdash_test.
-
-## [0.4.0] - 15-Jan-2019
-
-### Added
-
-- 256 color support.
-- Variable size container splits.
-- A more complete demo of the functionality.
-
-### Changed
-
-- Updated documentation and README.
-
-## [0.3.0] - 13-Jan-2019
-
-### Added
-
-- Primitives for drawing lines.
-- Implementation of a Braille canvas.
-- The LineChart widget.
-
-## [0.2.0] - 02-Jul-2018
-
-### Added
-
-- The SparkLine widget.
-- The BarChart widget.
-- Manually triggered redraw.
-- Travis now checks for presence of licence headers.
-
-### Fixed
-
-- Fixing races in termdash_test.
-
-## 0.1.0 - 13-Jun-2018
-
-### Added
-
-- Documentation of the project and its goals.
-- Drawing infrastructure.
-- Testing infrastructure.
-- The Gauge widget.
-- The Text widget.
-
-[unreleased]: https://github.com/mum4k/termdash/compare/v0.12.2...devel
-[0.12.2]: https://github.com/mum4k/termdash/compare/v0.12.1...v0.12.2
-[0.12.1]: https://github.com/mum4k/termdash/compare/v0.12.0...v0.12.1
-[0.12.0]: https://github.com/mum4k/termdash/compare/v0.11.0...v0.12.0
-[0.11.0]: https://github.com/mum4k/termdash/compare/v0.10.0...v0.11.0
-[0.10.0]: https://github.com/mum4k/termdash/compare/v0.9.1...v0.10.0
-[0.9.1]: https://github.com/mum4k/termdash/compare/v0.9.0...v0.9.1
-[0.9.0]: https://github.com/mum4k/termdash/compare/v0.8.0...v0.9.0
-[0.8.0]: https://github.com/mum4k/termdash/compare/v0.7.2...v0.8.0
-[0.7.2]: https://github.com/mum4k/termdash/compare/v0.7.1...v0.7.2
-[0.7.1]: https://github.com/mum4k/termdash/compare/v0.7.0...v0.7.1
-[0.7.0]: https://github.com/mum4k/termdash/compare/v0.6.1...v0.7.0
-[0.6.1]: https://github.com/mum4k/termdash/compare/v0.6.0...v0.6.1
-[0.6.0]: https://github.com/mum4k/termdash/compare/v0.5.0...v0.6.0
-[0.5.0]: https://github.com/mum4k/termdash/compare/v0.4.0...v0.5.0
-[0.4.0]: https://github.com/mum4k/termdash/compare/v0.3.0...v0.4.0
-[0.3.0]: https://github.com/mum4k/termdash/compare/v0.2.0...v0.3.0
-[0.2.0]: https://github.com/mum4k/termdash/compare/v0.1.0...v0.2.0
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/CONTRIBUTING.md b/examples/go-dashboard/src/github.com/mum4k/termdash/CONTRIBUTING.md
deleted file mode 100644
index 9f2027288..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/CONTRIBUTING.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# How to Contribute
-
-We'd love to accept your patches and contributions to this project. There are
-just a few small guidelines you need to follow.
-
-## Fork and merge into the "devel" branch
-
-All development in termdash repository must happen in the [devel
-branch](https://github.com/mum4k/termdash/tree/devel). The devel branch is
-merged into the master branch during release of each new version.
-
-When you fork the termdash repository, be sure to checkout the devel branch.
-When you are creating a pull request, be sure to pull back into the devel
-branch.
-
-## Contributor License Agreement
-
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution;
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to <https://cla.developers.google.com/> to see
-your current agreements on file or to sign a new one.
-
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
-
-## Code reviews
-
-All submissions, including submissions by project members, require review. We
-use GitHub pull requests for this purpose. Consult
-[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
-information on using pull requests.
-
-## Community Guidelines
-
-This project follows [Google's Open Source Community
-Guidelines](https://opensource.google.com/conduct/).
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/LICENSE b/examples/go-dashboard/src/github.com/mum4k/termdash/LICENSE
deleted file mode 100644
index 261eeb9e9..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/README.md b/examples/go-dashboard/src/github.com/mum4k/termdash/README.md
deleted file mode 100644
index 8ecd1fd53..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/README.md
+++ /dev/null
@@ -1,215 +0,0 @@
-[![Doc Status](https://godoc.org/github.com/mum4k/termdash?status.png)](https://godoc.org/github.com/mum4k/termdash)
-[![Build Status](https://travis-ci.com/mum4k/termdash.svg?branch=master)](https://travis-ci.com/mum4k/termdash)
-[![Sourcegraph](https://sourcegraph.com/github.com/mum4k/termdash/-/badge.svg)](https://sourcegraph.com/github.com/mum4k/termdash?badge)
-[![Coverage Status](https://coveralls.io/repos/github/mum4k/termdash/badge.svg?branch=master)](https://coveralls.io/github/mum4k/termdash?branch=master)
-[![Go Report Card](https://goreportcard.com/badge/github.com/mum4k/termdash)](https://goreportcard.com/report/github.com/mum4k/termdash)
-[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/mum4k/termdash/blob/master/LICENSE)
-[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)
-
-# [<img src="./doc/images/termdash.png" alt="termdashlogo" type="image/png" width="30%">](http://github.com/mum4k/termdash/wiki)
-
-Termdash is a cross-platform customizable terminal based dashboard.
-
-[<img src="./doc/images/termdashdemo_0_9_0.gif" alt="termdashdemo" type="image/gif">](termdashdemo/termdashdemo.go)
-
-The feature set is inspired by the
-[gizak/termui](http://github.com/gizak/termui) project, which in turn was
-inspired by
-[yaronn/blessed-contrib](http://github.com/yaronn/blessed-contrib).
-
-This rewrite focuses on code readability, maintainability and testability, see
-the [design goals](doc/design_goals.md). It aims to achieve the following
-[requirements](doc/requirements.md). See the [high-level design](doc/hld.md)
-for more details.
-
-# Public API and status
-
-The public API surface is documented in the
-[wiki](http://github.com/mum4k/termdash/wiki).
-
-Private packages can be identified by the presence of the **/private/**
-directory in their import path. Stability of the private packages isn't
-guaranteed and changes won't be backward compatible.
-
-There might still be breaking changes to the public API, at least until the
-project reaches version 1.0.0. Any breaking changes will be published in the
-[changelog](CHANGELOG.md).
-
-# Current feature set
-
-- Full support for terminal window resizing throughout the infrastructure.
-- Customizable layout, widget placement, borders, margins, padding, colors, etc.
-- Dynamic layout changes at runtime.
-- Binary tree and Grid forms of setting up the layout.
-- Focusable containers and widgets.
-- Processing of keyboard and mouse events.
-- Periodic and event driven screen redraw.
-- A library of widgets, see below.
-- UTF-8 for all text elements.
-- Drawing primitives (Go functions) for widget development with character and
- sub-character resolution.
-
-# Installation
-
-To install this library, run the following:
-
-```go
-go get -u github.com/mum4k/termdash
-```
-
-# Usage
-
-The usage of most of these elements is demonstrated in
-[termdashdemo.go](termdashdemo/termdashdemo.go). To execute the demo:
-
-```go
-go run github.com/mum4k/termdash/termdashdemo/termdashdemo.go
-```
-
-# Documentation
-
-Please refer to the [Termdash wiki](http://github.com/mum4k/termdash/wiki) for
-all documentation and resources.
-
-# Implemented Widgets
-
-## The Button
-
-Allows users to interact with the application, each button press runs a callback function.
-Run the
-[buttondemo](widgets/button/buttondemo/buttondemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/button/buttondemo/buttondemo.go
-```
-
-[<img src="./doc/images/buttondemo.gif" alt="buttondemo" type="image/gif" width="50%">](widgets/button/buttondemo/buttondemo.go)
-
-## The TextInput
-
-Allows users to interact with the application by entering, editing and
-submitting text data. Run the
-[textinputdemo](widgets/textinput/textinputdemo/textinputdemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/textinput/textinputdemo/textinputdemo.go
-```
-
-[<img src="./doc/images/textinputdemo.gif" alt="textinputdemo" type="image/gif" width="80%">](widgets/textinput/textinputdemo/textinputdemo.go)
-
-## The Gauge
-
-Displays the progress of an operation. Run the
-[gaugedemo](widgets/gauge/gaugedemo/gaugedemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/gauge/gaugedemo/gaugedemo.go
-```
-
-[<img src="./doc/images/gaugedemo.gif" alt="gaugedemo" type="image/gif">](widgets/gauge/gaugedemo/gaugedemo.go)
-
-## The Donut
-
-Visualizes progress of an operation as a partial or a complete donut. Run the
-[donutdemo](widgets/donut/donutdemo/donutdemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/donut/donutdemo/donutdemo.go
-```
-
-[<img src="./doc/images/donutdemo.gif" alt="donutdemo" type="image/gif">](widgets/donut/donutdemo/donutdemo.go)
-
-## The Text
-
-Displays text content, supports trimming and scrolling of content. Run the
-[textdemo](widgets/text/textdemo/textdemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/text/textdemo/textdemo.go
-```
-
-[<img src="./doc/images/textdemo.gif" alt="textdemo" type="image/gif">](widgets/text/textdemo/textdemo.go)
-
-## The SparkLine
-
-Draws a graph showing a series of values as vertical bars. The bars can have
-sub-cell height. Run the
-[sparklinedemo](widgets/sparkline/sparklinedemo/sparklinedemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/sparkline/sparklinedemo/sparklinedemo.go
-```
-
-[<img src="./doc/images/sparklinedemo.gif" alt="sparklinedemo" type="image/gif" width="50%">](widgets/sparkline/sparklinedemo/sparklinedemo.go)
-
-## The BarChart
-
-Displays multiple bars showing relative ratios of values. Run the
-[barchartdemo](widgets/barchart/barchartdemo/barchartdemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/barchart/barchartdemo/barchartdemo.go
-```
-
-[<img src="./doc/images/barchartdemo.gif" alt="barchartdemo" type="image/gif" width="50%">](widgets/barchart/barchartdemo/barchartdemo.go)
-
-## The LineChart
-
-Displays series of values on a line chart, supports zoom triggered by mouse
-events. Run the
-[linechartdemo](widgets/linechart/linechartdemo/linechartdemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/linechart/linechartdemo/linechartdemo.go
-```
-
-[<img src="./doc/images/linechartdemo.gif" alt="linechartdemo" type="image/gif" width="70%">](widgets/linechart/linechartdemo/linechartdemo.go)
-
-## The SegmentDisplay
-
-Displays text by simulating a 16-segment display. Run the
-[segmentdisplaydemo](widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go).
-
-```go
-go run github.com/mum4k/termdash/widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go
-```
-
-[<img src="./doc/images/segmentdisplaydemo.gif" alt="segmentdisplaydemo" type="image/gif">](widgets/segmentdisplay/segmentdisplaydemo/segmentdisplaydemo.go)
-
-# Contributing
-
-If you are willing to contribute, improve the infrastructure or develop a
-widget, first of all Thank You! Your help is appreciated.
-
-Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines related
-to the Google's CLA, and code review requirements.
-
-As stated above the primary goal of this project is to develop readable, well
-designed code, the functionality and efficiency come second. This is achieved
-through detailed code reviews, design discussions and following of the [design
-guidelines](doc/design_guidelines.md). Please familiarize yourself with these
-before contributing.
-
-If you're developing a new widget, please see the [widget
-development](doc/widget_development.md) section.
-
-Termdash uses [this branching model](https://nvie.com/posts/a-successful-git-branching-model/). When you fork the repository, base your changes off the [devel](https://github.com/mum4k/termdash/tree/devel) branch and the pull request should merge it back to the devel branch. Commits to the master branch are limited to releases, major bug fixes and documentation updates.
-
-# Similar projects in Go
-
-- [clui](https://github.com/VladimirMarkelov/clui)
-- [gocui](https://github.com/jroimartin/gocui)
-- [gowid](https://github.com/gcla/gowid)
-- [termui](https://github.com/gizak/termui)
-- [tui-go](https://github.com/marcusolsson/tui-go)
-- [tview](https://github.com/rivo/tview)
-
-# Projects using Termdash
-
-- [datadash](https://github.com/keithknott26/datadash): Visualize streaming or tabular data inside the terminal.
-- [grafterm](https://github.com/slok/grafterm): Metrics dashboards visualization on the terminal.
-- [perfstat](https://github.com/flaviostutz/perfstat): Analyze and show tips about possible bottlenecks in Linux systems.
-
-# Disclaimer
-
-This is not an official Google product.
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/align/align.go b/examples/go-dashboard/src/github.com/mum4k/termdash/align/align.go
deleted file mode 100644
index da4087f57..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/align/align.go
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package align defines constants representing types of alignment.
-package align
-
-// Horizontal indicates the type of horizontal alignment.
-type Horizontal int
-
-// String implements fmt.Stringer()
-func (h Horizontal) String() string {
- if n, ok := horizontalNames[h]; ok {
- return n
- }
- return "HorizontalUnknown"
-}
-
-// horizontalNames maps Horizontal values to human readable names.
-var horizontalNames = map[Horizontal]string{
- HorizontalLeft: "HorizontalLeft",
- HorizontalCenter: "HorizontalCenter",
- HorizontalRight: "HorizontalRight",
-}
-
-const (
- // HorizontalLeft is left alignment along the horizontal axis.
- HorizontalLeft Horizontal = iota
- // HorizontalCenter is center alignment along the horizontal axis.
- HorizontalCenter
- // HorizontalRight is right alignment along the horizontal axis.
- HorizontalRight
-)
-
-// Vertical indicates the type of vertical alignment.
-type Vertical int
-
-// String implements fmt.Stringer()
-func (v Vertical) String() string {
- if n, ok := verticalNames[v]; ok {
- return n
- }
- return "VerticalUnknown"
-}
-
-// verticalNames maps Vertical values to human readable names.
-var verticalNames = map[Vertical]string{
- VerticalTop: "VerticalTop",
- VerticalMiddle: "VerticalMiddle",
- VerticalBottom: "VerticalBottom",
-}
-
-const (
- // VerticalTop is top alignment along the vertical axis.
- VerticalTop Vertical = iota
- // VerticalMiddle is middle alignment along the vertical axis.
- VerticalMiddle
- // VerticalBottom is bottom alignment along the vertical axis.
- VerticalBottom
-)
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/cell/cell.go b/examples/go-dashboard/src/github.com/mum4k/termdash/cell/cell.go
deleted file mode 100644
index c3eb6df24..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/cell/cell.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package cell implements cell options and attributes.
-package cell
-
-// Option is used to provide options for cells on a 2-D terminal.
-type Option interface {
- // Set sets the provided option.
- Set(*Options)
-}
-
-// Options stores the provided options.
-type Options struct {
- FgColor Color
- BgColor Color
-}
-
-// Set allows existing options to be passed as an option.
-func (o *Options) Set(other *Options) {
- *other = *o
-}
-
-// NewOptions returns a new Options instance after applying the provided options.
-func NewOptions(opts ...Option) *Options {
- o := &Options{}
- for _, opt := range opts {
- opt.Set(o)
- }
- return o
-}
-
-// option implements Option.
-type option func(*Options)
-
-// Set implements Option.set.
-func (co option) Set(opts *Options) {
- co(opts)
-}
-
-// FgColor sets the foreground color of the cell.
-func FgColor(color Color) Option {
- return option(func(co *Options) {
- co.FgColor = color
- })
-}
-
-// BgColor sets the background color of the cell.
-func BgColor(color Color) Option {
- return option(func(co *Options) {
- co.BgColor = color
- })
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/cell/color.go b/examples/go-dashboard/src/github.com/mum4k/termdash/cell/color.go
deleted file mode 100644
index 94560b74c..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/cell/color.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package cell
-
-import (
- "fmt"
-)
-
-// color.go defines constants for cell colors.
-
-// Color is the color of a cell.
-type Color int
-
-// String implements fmt.Stringer()
-func (cc Color) String() string {
- if n, ok := colorNames[cc]; ok {
- return n
- }
- return fmt.Sprintf("Color:%d", cc)
-}
-
-// colorNames maps Color values to human readable names.
-var colorNames = map[Color]string{
- ColorDefault: "ColorDefault",
- ColorBlack: "ColorBlack",
- ColorRed: "ColorRed",
- ColorGreen: "ColorGreen",
- ColorYellow: "ColorYellow",
- ColorBlue: "ColorBlue",
- ColorMagenta: "ColorMagenta",
- ColorCyan: "ColorCyan",
- ColorWhite: "ColorWhite",
-}
-
-// The supported terminal colors.
-const (
- ColorDefault Color = iota
-
- // 8 "system" colors.
- ColorBlack
- ColorRed
- ColorGreen
- ColorYellow
- ColorBlue
- ColorMagenta
- ColorCyan
- ColorWhite
-)
-
-// ColorNumber sets a color using its number.
-// Make sure your terminal is set to a terminalapi.ColorMode that supports the
-// target color. The provided value must be in the range 0-255.
-// Larger or smaller values will be reset to the default color.
-//
-// For reference on these colors see the Xterm number in:
-// https://jonasjacek.github.io/colors/
-func ColorNumber(n int) Color {
- if n < 0 || n > 255 {
- return ColorDefault
- }
- return Color(n + 1) // Colors are off-by-one due to ColorDefault being zero.
-}
-
-// ColorRGB6 sets a color using the 6x6x6 terminal color.
-// Make sure your terminal is set to the terminalapi.ColorMode256 mode.
-// The provided values (r, g, b) must be in the range 0-5.
-// Larger or smaller values will be reset to the default color.
-//
-// For reference on these colors see:
-// https://superuser.com/questions/783656/whats-the-deal-with-terminal-colors
-func ColorRGB6(r, g, b int) Color {
- for _, c := range []int{r, g, b} {
- if c < 0 || c > 5 {
- return ColorDefault
- }
- }
- return Color(0x10 + 36*r + 6*g + b + 1) // Colors are off-by-one due to ColorDefault being zero.
-}
-
-// ColorRGB24 sets a color using the 24 bit web color scheme.
-// Make sure your terminal is set to the terminalapi.ColorMode256 mode.
-// The provided values (r, g, b) must be in the range 0-255.
-// Larger or smaller values will be reset to the default color.
-//
-// For reference on these colors see the RGB column in:
-// https://jonasjacek.github.io/colors/
-func ColorRGB24(r, g, b int) Color {
- for _, c := range []int{r, g, b} {
- if c < 0 || c > 255 {
- return ColorDefault
- }
- }
- return ColorRGB6(r/51, g/51, b/51)
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/container.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/container.go
deleted file mode 100644
index 54cef7856..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/container.go
+++ /dev/null
@@ -1,471 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-/*
-Package container defines a type that wraps other containers or widgets.
-
-The container supports splitting container into sub containers, defining
-container styles and placing widgets. The container also creates and manages
-canvases assigned to the placed widgets.
-*/
-package container
-
-import (
- "errors"
- "fmt"
- "image"
- "sync"
-
- "github.com/mum4k/termdash/linestyle"
- "github.com/mum4k/termdash/private/alignfor"
- "github.com/mum4k/termdash/private/area"
- "github.com/mum4k/termdash/private/event"
- "github.com/mum4k/termdash/terminal/terminalapi"
- "github.com/mum4k/termdash/widgetapi"
-)
-
-// Container wraps either sub containers or widgets and positions them on the
-// terminal.
-// This is thread-safe.
-type Container struct {
- // parent is the parent container, nil if this is the root container.
- parent *Container
- // The sub containers, if these aren't nil, the widget must be.
- first *Container
- second *Container
-
- // term is the terminal this container is placed on.
- // All containers in the tree share the same terminal.
- term terminalapi.Terminal
-
- // focusTracker tracks the active (focused) container.
- // All containers in the tree share the same tracker.
- focusTracker *focusTracker
-
- // area is the area of the terminal this container has access to.
- // Initialized the first time Draw is called.
- area image.Rectangle
-
- // opts are the options provided to the container.
- opts *options
-
- // clearNeeded indicates if the terminal needs to be cleared next time we
- // are clearNeeded the container.
- // This is required if the container was updated and thus the layout might
- // have changed.
- clearNeeded bool
-
- // mu protects the container tree.
- // All containers in the tree share the same lock.
- mu *sync.Mutex
-}
-
-// String represents the container metadata in a human readable format.
-// Implements fmt.Stringer.
-func (c *Container) String() string {
- return fmt.Sprintf("Container@%p{parent:%p, first:%p, second:%p, area:%+v}", c, c.parent, c.first, c.second, c.area)
-}
-
-// New returns a new root container that will use the provided terminal and
-// applies the provided options.
-func New(t terminalapi.Terminal, opts ...Option) (*Container, error) {
- root := &Container{
- term: t,
- opts: newOptions( /* parent = */ nil),
- mu: &sync.Mutex{},
- }
-
- // Initially the root is focused.
- root.focusTracker = newFocusTracker(root)
- if err := applyOptions(root, opts...); err != nil {
- return nil, err
- }
- if err := validateOptions(root); err != nil {
- return nil, err
- }
- return root, nil
-}
-
-// newChild creates a new child container of the given parent.
-func newChild(parent *Container, opts []Option) (*Container, error) {
- child := &Container{
- parent: parent,
- term: parent.term,
- focusTracker: parent.focusTracker,
- opts: newOptions(parent.opts),
- mu: parent.mu,
- }
- if err := applyOptions(child, opts...); err != nil {
- return nil, err
- }
- return child, nil
-}
-
-// hasBorder determines if this container has a border.
-func (c *Container) hasBorder() bool {
- return c.opts.border != linestyle.None
-}
-
-// hasWidget determines if this container has a widget.
-func (c *Container) hasWidget() bool {
- return c.opts.widget != nil
-}
-
-// usable returns the usable area in this container.
-// This depends on whether the container has a border, etc.
-func (c *Container) usable() image.Rectangle {
- if c.hasBorder() {
- return area.ExcludeBorder(c.area)
- }
- return c.area
-}
-
-// widgetArea returns the area in the container that is available for the
-// widget's canvas. Takes the container border, widget's requested maximum size
-// and ratio and container's alignment into account.
-// Returns a zero area if the container has no widget.
-func (c *Container) widgetArea() (image.Rectangle, error) {
- if !c.hasWidget() {
- return image.ZR, nil
- }
-
- padded, err := c.opts.padding.apply(c.usable())
- if err != nil {
- return image.ZR, err
- }
- wOpts := c.opts.widget.Options()
-
- adjusted := padded
- if maxX := wOpts.MaximumSize.X; maxX > 0 && adjusted.Dx() > maxX {
- adjusted.Max.X -= adjusted.Dx() - maxX
- }
- if maxY := wOpts.MaximumSize.Y; maxY > 0 && adjusted.Dy() > maxY {
- adjusted.Max.Y -= adjusted.Dy() - maxY
- }
-
- if wOpts.Ratio.X > 0 && wOpts.Ratio.Y > 0 {
- adjusted = area.WithRatio(adjusted, wOpts.Ratio)
- }
- aligned, err := alignfor.Rectangle(padded, adjusted, c.opts.hAlign, c.opts.vAlign)
- if err != nil {
- return image.ZR, err
- }
- return aligned, nil
-}
-
-// split splits the container's usable area into child areas.
-// Panics if the container isn't configured for a split.
-func (c *Container) split() (image.Rectangle, image.Rectangle, error) {
- ar, err := c.opts.padding.apply(c.usable())
- if err != nil {
- return image.ZR, image.ZR, err
- }
- if c.opts.splitFixed > DefaultSplitFixed {
- if c.opts.split == splitTypeVertical {
- return area.VSplitCells(ar, c.opts.splitFixed)
- }
- return area.HSplitCells(ar, c.opts.splitFixed)
- }
-
- if c.opts.split == splitTypeVertical {
- return area.VSplit(ar, c.opts.splitPercent)
- }
- return area.HSplit(ar, c.opts.splitPercent)
-}
-
-// createFirst creates and returns the first sub container of this container.
-func (c *Container) createFirst(opts []Option) error {
- first, err := newChild(c, opts)
- if err != nil {
- return err
- }
- c.first = first
- return nil
-}
-
-// createSecond creates and returns the second sub container of this container.
-func (c *Container) createSecond(opts []Option) error {
- second, err := newChild(c, opts)
- if err != nil {
- return err
- }
- c.second = second
- return nil
-}
-
-// Draw draws this container and all of its sub containers.
-func (c *Container) Draw() error {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- if c.clearNeeded {
- if err := c.term.Clear(); err != nil {
- return fmt.Errorf("term.Clear => error: %v", err)
- }
- c.clearNeeded = false
- }
-
- // Update the area we are tracking for focus in case the terminal size
- // changed.
- ar, err := area.FromSize(c.term.Size())
- if err != nil {
- return err
- }
- c.focusTracker.updateArea(ar)
- return drawTree(c)
-}
-
-// Update updates container with the specified id by setting the provided
-// options. This can be used to perform dynamic layout changes, i.e. anything
-// between replacing the widget in the container and completely changing the
-// layout and splits.
-// The argument id must match exactly one container with that was created with
-// matching ID() option. The argument id must not be an empty string.
-func (c *Container) Update(id string, opts ...Option) error {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- target, err := findID(c, id)
- if err != nil {
- return err
- }
- c.clearNeeded = true
-
- if err := applyOptions(target, opts...); err != nil {
- return err
- }
- if err := validateOptions(c); err != nil {
- return err
- }
-
- // The currently focused container might not be reachable anymore, because
- // it was under the target. If that is so, move the focus up to the target.
- if !c.focusTracker.reachableFrom(c) {
- c.focusTracker.setActive(target)
- }
- return nil
-}
-
-// updateFocus processes the mouse event and determines if it changes the
-// focused container.
-// Caller must hold c.mu.
-func (c *Container) updateFocus(m *terminalapi.Mouse) {
- target := pointCont(c, m.Position)
- if target == nil { // Ignore mouse clicks where no containers are.
- return
- }
- c.focusTracker.mouse(target, m)
-}
-
-// processEvent processes events delivered to the container.
-func (c *Container) processEvent(ev terminalapi.Event) error {
- // This is done in two stages.
- // 1) under lock we traverse the container and identify all targets
- // (widgets) that should receive the event.
- // 2) lock is released and events are delivered to the widgets. Widgets
- // themselves are thread-safe. Lock must be releases when delivering,
- // because some widgets might try to mutate the container when they
- // receive the event, like dynamically change the layout.
- c.mu.Lock()
- sendFn, err := c.prepareEvTargets(ev)
- c.mu.Unlock()
- if err != nil {
- return err
- }
- return sendFn()
-}
-
-// prepareEvTargets returns a closure, that when called delivers the event to
-// widgets that registered for it.
-// Also processes the event on behalf of the container (tracks keyboard focus).
-// Caller must hold c.mu.
-func (c *Container) prepareEvTargets(ev terminalapi.Event) (func() error, error) {
- switch e := ev.(type) {
- case *terminalapi.Mouse:
- c.updateFocus(ev.(*terminalapi.Mouse))
-
- targets, err := c.mouseEvTargets(e)
- if err != nil {
- return nil, err
- }
- return func() error {
- for _, mt := range targets {
- if err := mt.widget.Mouse(mt.ev); err != nil {
- return err
- }
- }
- return nil
- }, nil
-
- case *terminalapi.Keyboard:
- targets := c.keyEvTargets()
- return func() error {
- for _, w := range targets {
- if err := w.Keyboard(e); err != nil {
- return err
- }
- }
- return nil
- }, nil
-
- default:
- return nil, fmt.Errorf("container received an unsupported event type %T", ev)
- }
-}
-
-// keyEvTargets returns those widgets found in the container that should
-// receive this keyboard event.
-// Caller must hold c.mu.
-func (c *Container) keyEvTargets() []widgetapi.Widget {
- var (
- errStr string
- widgets []widgetapi.Widget
- )
-
- // All the widgets that should receive this event.
- // For now stable ordering (preOrder).
- preOrder(c, &errStr, visitFunc(func(cur *Container) error {
- if !cur.hasWidget() {
- return nil
- }
-
- wOpt := cur.opts.widget.Options()
- switch wOpt.WantKeyboard {
- case widgetapi.KeyScopeNone:
- // Widget doesn't want any keyboard events.
- return nil
-
- case widgetapi.KeyScopeFocused:
- if cur.focusTracker.isActive(cur) {
- widgets = append(widgets, cur.opts.widget)
- }
-
- case widgetapi.KeyScopeGlobal:
- widgets = append(widgets, cur.opts.widget)
- }
- return nil
- }))
- return widgets
-}
-
-// mouseEvTarget contains a mouse event adjusted relative to the widget's area
-// and the widget that should receive it.
-type mouseEvTarget struct {
- // widget is the widget that should receive the mouse event.
- widget widgetapi.Widget
- // ev is the adjusted mouse event.
- ev *terminalapi.Mouse
-}
-
-// newMouseEvTarget returns a new newMouseEvTarget.
-func newMouseEvTarget(w widgetapi.Widget, wArea image.Rectangle, ev *terminalapi.Mouse) *mouseEvTarget {
- return &mouseEvTarget{
- widget: w,
- ev: adjustMouseEv(ev, wArea),
- }
-}
-
-// mouseEvTargets returns those widgets found in the container that should
-// receive this mouse event.
-// Caller must hold c.mu.
-func (c *Container) mouseEvTargets(m *terminalapi.Mouse) ([]*mouseEvTarget, error) {
- var (
- errStr string
- widgets []*mouseEvTarget
- )
-
- // All the widgets that should receive this event.
- // For now stable ordering (preOrder).
- preOrder(c, &errStr, visitFunc(func(cur *Container) error {
- if !cur.hasWidget() {
- return nil
- }
-
- wOpts := cur.opts.widget.Options()
- wa, err := cur.widgetArea()
- if err != nil {
- return err
- }
-
- switch wOpts.WantMouse {
- case widgetapi.MouseScopeNone:
- // Widget doesn't want any mouse events.
- return nil
-
- case widgetapi.MouseScopeWidget:
- // Only if the event falls inside of the widget's canvas.
- if m.Position.In(wa) {
- widgets = append(widgets, newMouseEvTarget(cur.opts.widget, wa, m))
- }
-
- case widgetapi.MouseScopeContainer:
- // Only if the event falls inside the widget's parent container.
- if m.Position.In(cur.area) {
- widgets = append(widgets, newMouseEvTarget(cur.opts.widget, wa, m))
- }
-
- case widgetapi.MouseScopeGlobal:
- // Widget wants all mouse events.
- widgets = append(widgets, newMouseEvTarget(cur.opts.widget, wa, m))
- }
- return nil
- }))
-
- if errStr != "" {
- return nil, errors.New(errStr)
- }
- return widgets, nil
-}
-
-// Subscribe tells the container to subscribe itself and widgets to the
-// provided event distribution system.
-// This method is private to termdash, stability isn't guaranteed and changes
-// won't be backward compatible.
-func (c *Container) Subscribe(eds *event.DistributionSystem) {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- // maxReps is the maximum number of repetitive events towards widgets
- // before we throttle them.
- const maxReps = 10
-
- // Subscriber the container itself in order to track keyboard focus.
- want := []terminalapi.Event{
- &terminalapi.Keyboard{},
- &terminalapi.Mouse{},
- }
- eds.Subscribe(want, func(ev terminalapi.Event) {
- if err := c.processEvent(ev); err != nil {
- eds.Event(terminalapi.NewErrorf("failed to process event %v: %v", ev, err))
- }
- }, event.MaxRepetitive(maxReps))
-}
-
-// adjustMouseEv adjusts the mouse event relative to the widget area.
-func adjustMouseEv(m *terminalapi.Mouse, wArea image.Rectangle) *terminalapi.Mouse {
- // The sent mouse coordinate is relative to the widget canvas, i.e. zero
- // based, even though the widget might not be in the top left corner on the
- // terminal.
- offset := wArea.Min
- if m.Position.In(wArea) {
- return &terminalapi.Mouse{
- Position: m.Position.Sub(offset),
- Button: m.Button,
- }
- }
- return &terminalapi.Mouse{
- Position: image.Point{-1, -1},
- Button: m.Button,
- }
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/draw.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/draw.go
deleted file mode 100644
index d186b1272..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/draw.go
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package container
-
-// draw.go contains logic to draw containers and the contained widgets.
-
-import (
- "errors"
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/area"
- "github.com/mum4k/termdash/private/canvas"
- "github.com/mum4k/termdash/private/draw"
- "github.com/mum4k/termdash/widgetapi"
-)
-
-// drawTree draws this container and all of its sub containers.
-func drawTree(c *Container) error {
- var errStr string
-
- root := rootCont(c)
- size := root.term.Size()
- ar, err := root.opts.margin.apply(image.Rect(0, 0, size.X, size.Y))
- if err != nil {
- return err
- }
- root.area = ar
-
- preOrder(root, &errStr, visitFunc(func(c *Container) error {
- first, second, err := c.split()
- if err != nil {
- return err
- }
- if c.first != nil {
- ar, err := c.first.opts.margin.apply(first)
- if err != nil {
- return err
- }
- c.first.area = ar
- }
-
- if c.second != nil {
- ar, err := c.second.opts.margin.apply(second)
- if err != nil {
- return err
- }
- c.second.area = ar
- }
- return drawCont(c)
- }))
- if errStr != "" {
- return errors.New(errStr)
- }
- return nil
-}
-
-// drawBorder draws the border around the container if requested.
-func drawBorder(c *Container) error {
- if !c.hasBorder() {
- return nil
- }
-
- cvs, err := canvas.New(c.area)
- if err != nil {
- return err
- }
-
- ar, err := area.FromSize(cvs.Size())
- if err != nil {
- return err
- }
-
- var cOpts []cell.Option
- if c.focusTracker.isActive(c) {
- cOpts = append(cOpts, cell.FgColor(c.opts.inherited.focusedColor))
- } else {
- cOpts = append(cOpts, cell.FgColor(c.opts.inherited.borderColor))
- }
-
- if err := draw.Border(cvs, ar,
- draw.BorderLineStyle(c.opts.border),
- draw.BorderTitle(c.opts.borderTitle, draw.OverrunModeThreeDot, cOpts...),
- draw.BorderTitleAlign(c.opts.borderTitleHAlign),
- draw.BorderCellOpts(cOpts...),
- ); err != nil {
- return err
- }
- return cvs.Apply(c.term)
-}
-
-// drawWidget requests the widget to draw on the canvas.
-func drawWidget(c *Container) error {
- widgetArea, err := c.widgetArea()
- if err != nil {
- return err
- }
- if widgetArea == image.ZR {
- return nil
- }
-
- if !c.hasWidget() {
- return nil
- }
-
- needSize := image.Point{1, 1}
- wOpts := c.opts.widget.Options()
- if wOpts.MinimumSize.X > 0 && wOpts.MinimumSize.Y > 0 {
- needSize = wOpts.MinimumSize
- }
-
- if widgetArea.Dx() < needSize.X || widgetArea.Dy() < needSize.Y {
- return drawResize(c, c.usable())
- }
-
- cvs, err := canvas.New(widgetArea)
- if err != nil {
- return err
- }
-
- meta := &widgetapi.Meta{
- Focused: c.focusTracker.isActive(c),
- }
-
- if err := c.opts.widget.Draw(cvs, meta); err != nil {
- return err
- }
- return cvs.Apply(c.term)
-}
-
-// drawResize draws an unicode character indicating that the size is too small to draw this container.
-// Does nothing if the size is smaller than one cell, leaving no space for the character.
-func drawResize(c *Container, area image.Rectangle) error {
- if area.Dx() < 1 || area.Dy() < 1 {
- return nil
- }
-
- cvs, err := canvas.New(area)
- if err != nil {
- return err
- }
- if err := draw.ResizeNeeded(cvs); err != nil {
- return err
- }
- return cvs.Apply(c.term)
-}
-
-// drawCont draws the container and its widget.
-func drawCont(c *Container) error {
- if us := c.usable(); us.Dx() <= 0 || us.Dy() <= 0 {
- return drawResize(c, c.area)
- }
-
- if err := drawBorder(c); err != nil {
- return fmt.Errorf("unable to draw container border: %v", err)
- }
-
- if err := drawWidget(c); err != nil {
- return fmt.Errorf("unable to draw widget %T: %v", c.opts.widget, err)
- }
- return nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/focus.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/focus.go
deleted file mode 100644
index 4320eea73..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/focus.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package container
-
-// focus.go contains code that tracks the focused container.
-
-import (
- "image"
-
- "github.com/mum4k/termdash/mouse"
- "github.com/mum4k/termdash/private/button"
- "github.com/mum4k/termdash/terminal/terminalapi"
-)
-
-// pointCont finds the top-most (on the screen) container whose area contains
-// the given point. Returns nil if none of the containers in the tree contain
-// this point.
-func pointCont(c *Container, p image.Point) *Container {
- var (
- errStr string
- cont *Container
- )
- postOrder(rootCont(c), &errStr, visitFunc(func(c *Container) error {
- if p.In(c.area) && cont == nil {
- cont = c
- }
- return nil
- }))
- return cont
-}
-
-// focusTracker tracks the active (focused) container.
-// This is not thread-safe, the implementation assumes that the owner of
-// focusTracker performs locking.
-type focusTracker struct {
- // container is the currently focused container.
- container *Container
-
- // candidate is the container that might become focused next. I.e. we got
- // a mouse click and now waiting for a release or a timeout.
- candidate *Container
-
- // buttonFSM is a state machine tracking mouse clicks in containers and
- // moving focus from one container to the next.
- buttonFSM *button.FSM
-}
-
-// newFocusTracker returns a new focus tracker with focus set at the provided
-// container.
-func newFocusTracker(c *Container) *focusTracker {
- return &focusTracker{
- container: c,
- // Mouse FSM tracking clicks inside the entire area for the root
- // container.
- buttonFSM: button.NewFSM(mouse.ButtonLeft, c.area),
- }
-}
-
-// isActive determines if the provided container is the currently active container.
-func (ft *focusTracker) isActive(c *Container) bool {
- return ft.container == c
-}
-
-// setActive sets the currently active container to the one provided.
-func (ft *focusTracker) setActive(c *Container) {
- ft.container = c
-}
-
-// mouse identifies mouse events that change the focused container and track
-// the focused container in the tree.
-// The argument c is the container onto which the mouse event landed.
-func (ft *focusTracker) mouse(target *Container, m *terminalapi.Mouse) {
- clicked, bs := ft.buttonFSM.Event(m)
- switch {
- case bs == button.Down:
- ft.candidate = target
- case bs == button.Up && clicked:
- if target == ft.candidate {
- ft.container = target
- }
- }
-}
-
-// updateArea updates the area that the focus tracker considers active for
-// mouse clicks.
-func (ft *focusTracker) updateArea(ar image.Rectangle) {
- ft.buttonFSM.UpdateArea(ar)
-}
-
-// reachableFrom asserts whether the currently focused container is reachable
-// from the provided node in the tree.
-func (ft *focusTracker) reachableFrom(node *Container) bool {
- var (
- errStr string
- reachable bool
- )
- preOrder(node, &errStr, visitFunc(func(c *Container) error {
- if c == ft.container {
- reachable = true
- }
- return nil
- }))
- return reachable
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/options.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/options.go
deleted file mode 100644
index 2d34af4db..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/options.go
+++ /dev/null
@@ -1,817 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package container
-
-// options.go defines container options.
-
-import (
- "errors"
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/align"
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/linestyle"
- "github.com/mum4k/termdash/private/area"
- "github.com/mum4k/termdash/widgetapi"
-)
-
-// applyOptions applies the options to the container and validates them.
-func applyOptions(c *Container, opts ...Option) error {
- for _, opt := range opts {
- if err := opt.set(c); err != nil {
- return err
- }
- }
- return nil
-}
-
-// ensure all the container identifiers are either empty or unique.
-func validateIds(c *Container, seen map[string]bool) error {
- if c.opts.id == "" {
- return nil
- } else if seen[c.opts.id] {
- return fmt.Errorf("duplicate container ID %q", c.opts.id)
- }
- seen[c.opts.id] = true
-
- return nil
-}
-
-// ensure all the container only have one split modifier.
-func validateSplits(c *Container) error {
- if c.opts.splitFixed > DefaultSplitFixed && c.opts.splitPercent != DefaultSplitPercent {
- return fmt.Errorf(
- "only one of splitFixed `%v` and splitPercent `%v` is allowed to be set per container",
- c.opts.splitFixed,
- c.opts.splitPercent,
- )
- }
-
- return nil
-}
-
-// validateOptions validates options set in the container tree.
-func validateOptions(c *Container) error {
- var errStr string
- seenID := map[string]bool{}
- preOrder(c, &errStr, func(c *Container) error {
- if err := validateIds(c, seenID); err != nil {
- return err
- }
- if err := validateSplits(c); err != nil {
- return err
- }
-
- return nil
- })
- if errStr != "" {
- return errors.New(errStr)
- }
-
- return nil
-}
-
-// Option is used to provide options to a container.
-type Option interface {
- // set sets the provided option.
- set(*Container) error
-}
-
-// options stores the options provided to the container.
-type options struct {
- // id is the identifier provided by the user.
- id string
-
- // inherited are options that are inherited by child containers.
- inherited inherited
-
- // split identifies how is this container split.
- split splitType
- splitPercent int
- splitFixed int
-
- // widget is the widget in the container.
- // A container can have either two sub containers (left and right) or a
- // widget. But not both.
- widget widgetapi.Widget
-
- // Alignment of the widget if present.
- hAlign align.Horizontal
- vAlign align.Vertical
-
- // border is the border around the container.
- border linestyle.LineStyle
- borderTitle string
- borderTitleHAlign align.Horizontal
-
- // padding is a space reserved between the outer edge of the container and
- // its content (the widget or other sub-containers).
- padding padding
-
- // margin is a space reserved on the outside of the container.
- margin margin
-}
-
-// margin stores the configured margin for the container.
-// For each margin direction, only one of the percentage or cells is set.
-type margin struct {
- topCells int
- topPerc int
- rightCells int
- rightPerc int
- bottomCells int
- bottomPerc int
- leftCells int
- leftPerc int
-}
-
-// apply applies the configured margin to the area.
-func (p *margin) apply(ar image.Rectangle) (image.Rectangle, error) {
- switch {
- case p.topCells != 0 || p.rightCells != 0 || p.bottomCells != 0 || p.leftCells != 0:
- return area.Shrink(ar, p.topCells, p.rightCells, p.bottomCells, p.leftCells)
- case p.topPerc != 0 || p.rightPerc != 0 || p.bottomPerc != 0 || p.leftPerc != 0:
- return area.ShrinkPercent(ar, p.topPerc, p.rightPerc, p.bottomPerc, p.leftPerc)
- }
- return ar, nil
-}
-
-// padding stores the configured padding for the container.
-// For each padding direction, only one of the percentage or cells is set.
-type padding struct {
- topCells int
- topPerc int
- rightCells int
- rightPerc int
- bottomCells int
- bottomPerc int
- leftCells int
- leftPerc int
-}
-
-// apply applies the configured padding to the area.
-func (p *padding) apply(ar image.Rectangle) (image.Rectangle, error) {
- switch {
- case p.topCells != 0 || p.rightCells != 0 || p.bottomCells != 0 || p.leftCells != 0:
- return area.Shrink(ar, p.topCells, p.rightCells, p.bottomCells, p.leftCells)
- case p.topPerc != 0 || p.rightPerc != 0 || p.bottomPerc != 0 || p.leftPerc != 0:
- return area.ShrinkPercent(ar, p.topPerc, p.rightPerc, p.bottomPerc, p.leftPerc)
- }
- return ar, nil
-}
-
-// inherited contains options that are inherited by child containers.
-type inherited struct {
- // borderColor is the color used for the border.
- borderColor cell.Color
- // focusedColor is the color used for the border when focused.
- focusedColor cell.Color
-}
-
-// newOptions returns a new options instance with the default values.
-// Parent are the inherited options from the parent container or nil if these
-// options are for a container with no parent (the root).
-func newOptions(parent *options) *options {
- opts := &options{
- inherited: inherited{
- focusedColor: cell.ColorYellow,
- },
- hAlign: align.HorizontalCenter,
- vAlign: align.VerticalMiddle,
- splitPercent: DefaultSplitPercent,
- splitFixed: DefaultSplitFixed,
- }
- if parent != nil {
- opts.inherited = parent.inherited
- }
- return opts
-}
-
-// option implements Option.
-type option func(*Container) error
-
-// set implements Option.set.
-func (o option) set(c *Container) error {
- return o(c)
-}
-
-// SplitOption is used when splitting containers.
-type SplitOption interface {
- // setSplit sets the provided split option.
- setSplit(*options) error
-}
-
-// splitOption implements SplitOption.
-type splitOption func(*options) error
-
-// setSplit implements SplitOption.setSplit.
-func (so splitOption) setSplit(opts *options) error {
- return so(opts)
-}
-
-// DefaultSplitPercent is the default value for the SplitPercent option.
-const DefaultSplitPercent = 50
-
-// DefaultSplitFixed is the default value for the SplitFixed option.
-const DefaultSplitFixed = -1
-
-// SplitPercent sets the relative size of the split as percentage of the available space.
-// When using SplitVertical, the provided size is applied to the new left
-// container, the new right container gets the reminder of the size.
-// When using SplitHorizontal, the provided size is applied to the new top
-// container, the new bottom container gets the reminder of the size.
-// The provided value must be a positive number in the range 0 < p < 100.
-// If not provided, defaults to DefaultSplitPercent.
-func SplitPercent(p int) SplitOption {
- return splitOption(func(opts *options) error {
- if min, max := 0, 100; p <= min || p >= max {
- return fmt.Errorf("invalid split percentage %d, must be in range %d < p < %d", p, min, max)
- }
- opts.splitPercent = p
- return nil
- })
-}
-
-// SplitFixed sets the size of the first container to be a fixed value
-// and makes the second container take up the remaining space.
-// When using SplitVertical, the provided size is applied to the new left
-// container, the new right container gets the reminder of the size.
-// When using SplitHorizontal, the provided size is applied to the new top
-// container, the new bottom container gets the reminder of the size.
-// The provided value must be a positive number in the range 0 <= cells.
-// If SplitFixed() is not specified, it defaults to SplitPercent() and its given value.
-// Only one of SplitFixed() and SplitPercent() can be specified per container.
-func SplitFixed(cells int) SplitOption {
- return splitOption(func(opts *options) error {
- if cells < 0 {
- return fmt.Errorf("invalid fixed value %d, must be in range %d <= cells", cells, 0)
- }
- opts.splitFixed = cells
- return nil
- })
-}
-
-// SplitVertical splits the container along the vertical axis into two sub
-// containers. The use of this option removes any widget placed at this
-// container, containers with sub containers cannot contain widgets.
-func SplitVertical(l LeftOption, r RightOption, opts ...SplitOption) Option {
- return option(func(c *Container) error {
- c.opts.split = splitTypeVertical
- c.opts.widget = nil
- for _, opt := range opts {
- if err := opt.setSplit(c.opts); err != nil {
- return err
- }
- }
-
- if err := c.createFirst(l.lOpts()); err != nil {
- return err
- }
- return c.createSecond(r.rOpts())
- })
-}
-
-// SplitHorizontal splits the container along the horizontal axis into two sub
-// containers. The use of this option removes any widget placed at this
-// container, containers with sub containers cannot contain widgets.
-func SplitHorizontal(t TopOption, b BottomOption, opts ...SplitOption) Option {
- return option(func(c *Container) error {
- c.opts.split = splitTypeHorizontal
- c.opts.widget = nil
- for _, opt := range opts {
- if err := opt.setSplit(c.opts); err != nil {
- return err
- }
- }
-
- if err := c.createFirst(t.tOpts()); err != nil {
- return err
- }
-
- return c.createSecond(b.bOpts())
- })
-}
-
-// ID sets an identifier for this container.
-// This ID can be later used to perform dynamic layout changes by passing new
-// options to this container. When provided, it must be a non-empty string that
-// is unique among all the containers.
-func ID(id string) Option {
- return option(func(c *Container) error {
- if id == "" {
- return errors.New("the ID cannot be an empty string")
- }
- c.opts.id = id
- return nil
- })
-}
-
-// Clear clears this container.
-// If the container contains a widget, the widget is removed.
-// If the container had any sub containers or splits, they are removed.
-func Clear() Option {
- return option(func(c *Container) error {
- c.opts.widget = nil
- c.first = nil
- c.second = nil
- return nil
- })
-}
-
-// PlaceWidget places the provided widget into the container.
-// The use of this option removes any sub containers. Containers with sub
-// containers cannot have widgets.
-func PlaceWidget(w widgetapi.Widget) Option {
- return option(func(c *Container) error {
- c.opts.widget = w
- c.first = nil
- c.second = nil
- return nil
- })
-}
-
-// MarginTop sets reserved space outside of the container at its top.
-// The provided number is the absolute margin in cells and must be zero or a
-// positive integer. Only one of MarginTop or MarginTopPercent can be specified.
-func MarginTop(cells int) Option {
- return option(func(c *Container) error {
- if min := 0; cells < min {
- return fmt.Errorf("invalid MarginTop(%d), must be in range %d <= value", cells, min)
- }
- if c.opts.margin.topPerc > 0 {
- return fmt.Errorf("cannot specify both MarginTop(%d) and MarginTopPercent(%d)", cells, c.opts.margin.topPerc)
- }
- c.opts.margin.topCells = cells
- return nil
- })
-}
-
-// MarginRight sets reserved space outside of the container at its right.
-// The provided number is the absolute margin in cells and must be zero or a
-// positive integer. Only one of MarginRight or MarginRightPercent can be specified.
-func MarginRight(cells int) Option {
- return option(func(c *Container) error {
- if min := 0; cells < min {
- return fmt.Errorf("invalid MarginRight(%d), must be in range %d <= value", cells, min)
- }
- if c.opts.margin.rightPerc > 0 {
- return fmt.Errorf("cannot specify both MarginRight(%d) and MarginRightPercent(%d)", cells, c.opts.margin.rightPerc)
- }
- c.opts.margin.rightCells = cells
- return nil
- })
-}
-
-// MarginBottom sets reserved space outside of the container at its bottom.
-// The provided number is the absolute margin in cells and must be zero or a
-// positive integer. Only one of MarginBottom or MarginBottomPercent can be specified.
-func MarginBottom(cells int) Option {
- return option(func(c *Container) error {
- if min := 0; cells < min {
- return fmt.Errorf("invalid MarginBottom(%d), must be in range %d <= value", cells, min)
- }
- if c.opts.margin.bottomPerc > 0 {
- return fmt.Errorf("cannot specify both MarginBottom(%d) and MarginBottomPercent(%d)", cells, c.opts.margin.bottomPerc)
- }
- c.opts.margin.bottomCells = cells
- return nil
- })
-}
-
-// MarginLeft sets reserved space outside of the container at its left.
-// The provided number is the absolute margin in cells and must be zero or a
-// positive integer. Only one of MarginLeft or MarginLeftPercent can be specified.
-func MarginLeft(cells int) Option {
- return option(func(c *Container) error {
- if min := 0; cells < min {
- return fmt.Errorf("invalid MarginLeft(%d), must be in range %d <= value", cells, min)
- }
- if c.opts.margin.leftPerc > 0 {
- return fmt.Errorf("cannot specify both MarginLeft(%d) and MarginLeftPercent(%d)", cells, c.opts.margin.leftPerc)
- }
- c.opts.margin.leftCells = cells
- return nil
- })
-}
-
-// MarginTopPercent sets reserved space outside of the container at its top.
-// The provided number is a relative margin defined as percentage of the container's height.
-// Only one of MarginTop or MarginTopPercent can be specified.
-// The value must be in range 0 <= value <= 100.
-func MarginTopPercent(perc int) Option {
- return option(func(c *Container) error {
- if min, max := 0, 100; perc < min || perc > max {
- return fmt.Errorf("invalid MarginTopPercent(%d), must be in range %d <= value <= %d", perc, min, max)
- }
- if c.opts.margin.topCells > 0 {
- return fmt.Errorf("cannot specify both MarginTopPercent(%d) and MarginTop(%d)", perc, c.opts.margin.topCells)
- }
- c.opts.margin.topPerc = perc
- return nil
- })
-}
-
-// MarginRightPercent sets reserved space outside of the container at its right.
-// The provided number is a relative margin defined as percentage of the container's height.
-// Only one of MarginRight or MarginRightPercent can be specified.
-// The value must be in range 0 <= value <= 100.
-func MarginRightPercent(perc int) Option {
- return option(func(c *Container) error {
- if min, max := 0, 100; perc < min || perc > max {
- return fmt.Errorf("invalid MarginRightPercent(%d), must be in range %d <= value <= %d", perc, min, max)
- }
- if c.opts.margin.rightCells > 0 {
- return fmt.Errorf("cannot specify both MarginRightPercent(%d) and MarginRight(%d)", perc, c.opts.margin.rightCells)
- }
- c.opts.margin.rightPerc = perc
- return nil
- })
-}
-
-// MarginBottomPercent sets reserved space outside of the container at its bottom.
-// The provided number is a relative margin defined as percentage of the container's height.
-// Only one of MarginBottom or MarginBottomPercent can be specified.
-// The value must be in range 0 <= value <= 100.
-func MarginBottomPercent(perc int) Option {
- return option(func(c *Container) error {
- if min, max := 0, 100; perc < min || perc > max {
- return fmt.Errorf("invalid MarginBottomPercent(%d), must be in range %d <= value <= %d", perc, min, max)
- }
- if c.opts.margin.bottomCells > 0 {
- return fmt.Errorf("cannot specify both MarginBottomPercent(%d) and MarginBottom(%d)", perc, c.opts.margin.bottomCells)
- }
- c.opts.margin.bottomPerc = perc
- return nil
- })
-}
-
-// MarginLeftPercent sets reserved space outside of the container at its left.
-// The provided number is a relative margin defined as percentage of the container's height.
-// Only one of MarginLeft or MarginLeftPercent can be specified.
-// The value must be in range 0 <= value <= 100.
-func MarginLeftPercent(perc int) Option {
- return option(func(c *Container) error {
- if min, max := 0, 100; perc < min || perc > max {
- return fmt.Errorf("invalid MarginLeftPercent(%d), must be in range %d <= value <= %d", perc, min, max)
- }
- if c.opts.margin.leftCells > 0 {
- return fmt.Errorf("cannot specify both MarginLeftPercent(%d) and MarginLeft(%d)", perc, c.opts.margin.leftCells)
- }
- c.opts.margin.leftPerc = perc
- return nil
- })
-}
-
-// PaddingTop sets reserved space between container and the top side of its widget.
-// The widget's area size is decreased to accommodate the padding.
-// The provided number is the absolute padding in cells and must be zero or a
-// positive integer. Only one of PaddingTop or PaddingTopPercent can be specified.
-func PaddingTop(cells int) Option {
- return option(func(c *Container) error {
- if min := 0; cells < min {
- return fmt.Errorf("invalid PaddingTop(%d), must be in range %d <= value", cells, min)
- }
- if c.opts.padding.topPerc > 0 {
- return fmt.Errorf("cannot specify both PaddingTop(%d) and PaddingTopPercent(%d)", cells, c.opts.padding.topPerc)
- }
- c.opts.padding.topCells = cells
- return nil
- })
-}
-
-// PaddingRight sets reserved space between container and the right side of its widget.
-// The widget's area size is decreased to accommodate the padding.
-// The provided number is the absolute padding in cells and must be zero or a
-// positive integer. Only one of PaddingRight or PaddingRightPercent can be specified.
-func PaddingRight(cells int) Option {
- return option(func(c *Container) error {
- if min := 0; cells < min {
- return fmt.Errorf("invalid PaddingRight(%d), must be in range %d <= value", cells, min)
- }
- if c.opts.padding.rightPerc > 0 {
- return fmt.Errorf("cannot specify both PaddingRight(%d) and PaddingRightPercent(%d)", cells, c.opts.padding.rightPerc)
- }
- c.opts.padding.rightCells = cells
- return nil
- })
-}
-
-// PaddingBottom sets reserved space between container and the bottom side of its widget.
-// The widget's area size is decreased to accommodate the padding.
-// The provided number is the absolute padding in cells and must be zero or a
-// positive integer. Only one of PaddingBottom or PaddingBottomPercent can be specified.
-func PaddingBottom(cells int) Option {
- return option(func(c *Container) error {
- if min := 0; cells < min {
- return fmt.Errorf("invalid PaddingBottom(%d), must be in range %d <= value", cells, min)
- }
- if c.opts.padding.bottomPerc > 0 {
- return fmt.Errorf("cannot specify both PaddingBottom(%d) and PaddingBottomPercent(%d)", cells, c.opts.padding.bottomPerc)
- }
- c.opts.padding.bottomCells = cells
- return nil
- })
-}
-
-// PaddingLeft sets reserved space between container and the left side of its widget.
-// The widget's area size is decreased to accommodate the padding.
-// The provided number is the absolute padding in cells and must be zero or a
-// positive integer. Only one of PaddingLeft or PaddingLeftPercent can be specified.
-func PaddingLeft(cells int) Option {
- return option(func(c *Container) error {
- if min := 0; cells < min {
- return fmt.Errorf("invalid PaddingLeft(%d), must be in range %d <= value", cells, min)
- }
- if c.opts.padding.leftPerc > 0 {
- return fmt.Errorf("cannot specify both PaddingLeft(%d) and PaddingLeftPercent(%d)", cells, c.opts.padding.leftPerc)
- }
- c.opts.padding.leftCells = cells
- return nil
- })
-}
-
-// PaddingTopPercent sets reserved space between container and the top side of
-// its widget. The widget's area size is decreased to accommodate the padding.
-// The provided number is a relative padding defined as percentage of the
-// container's height. The value must be in range 0 <= value <= 100.
-// Only one of PaddingTop or PaddingTopPercent can be specified.
-func PaddingTopPercent(perc int) Option {
- return option(func(c *Container) error {
- if min, max := 0, 100; perc < min || perc > max {
- return fmt.Errorf("invalid PaddingTopPercent(%d), must be in range %d <= value <= %d", perc, min, max)
- }
- if c.opts.padding.topCells > 0 {
- return fmt.Errorf("cannot specify both PaddingTopPercent(%d) and PaddingTop(%d)", perc, c.opts.padding.topCells)
- }
- c.opts.padding.topPerc = perc
- return nil
- })
-}
-
-// PaddingRightPercent sets reserved space between container and the right side of
-// its widget. The widget's area size is decreased to accommodate the padding.
-// The provided number is a relative padding defined as percentage of the
-// container's width. The value must be in range 0 <= value <= 100.
-// Only one of PaddingRight or PaddingRightPercent can be specified.
-func PaddingRightPercent(perc int) Option {
- return option(func(c *Container) error {
- if min, max := 0, 100; perc < min || perc > max {
- return fmt.Errorf("invalid PaddingRightPercent(%d), must be in range %d <= value <= %d", perc, min, max)
- }
- if c.opts.padding.rightCells > 0 {
- return fmt.Errorf("cannot specify both PaddingRightPercent(%d) and PaddingRight(%d)", perc, c.opts.padding.rightCells)
- }
- c.opts.padding.rightPerc = perc
- return nil
- })
-}
-
-// PaddingBottomPercent sets reserved space between container and the bottom side of
-// its widget. The widget's area size is decreased to accommodate the padding.
-// The provided number is a relative padding defined as percentage of the
-// container's height. The value must be in range 0 <= value <= 100.
-// Only one of PaddingBottom or PaddingBottomPercent can be specified.
-func PaddingBottomPercent(perc int) Option {
- return option(func(c *Container) error {
- if min, max := 0, 100; perc < min || perc > max {
- return fmt.Errorf("invalid PaddingBottomPercent(%d), must be in range %d <= value <= %d", perc, min, max)
- }
- if c.opts.padding.bottomCells > 0 {
- return fmt.Errorf("cannot specify both PaddingBottomPercent(%d) and PaddingBottom(%d)", perc, c.opts.padding.bottomCells)
- }
- c.opts.padding.bottomPerc = perc
- return nil
- })
-}
-
-// PaddingLeftPercent sets reserved space between container and the left side of
-// its widget. The widget's area size is decreased to accommodate the padding.
-// The provided number is a relative padding defined as percentage of the
-// container's width. The value must be in range 0 <= value <= 100.
-// Only one of PaddingLeft or PaddingLeftPercent can be specified.
-func PaddingLeftPercent(perc int) Option {
- return option(func(c *Container) error {
- if min, max := 0, 100; perc < min || perc > max {
- return fmt.Errorf("invalid PaddingLeftPercent(%d), must be in range %d <= value <= %d", perc, min, max)
- }
- if c.opts.padding.leftCells > 0 {
- return fmt.Errorf("cannot specify both PaddingLeftPercent(%d) and PaddingLeft(%d)", perc, c.opts.padding.leftCells)
- }
- c.opts.padding.leftPerc = perc
- return nil
- })
-}
-
-// AlignHorizontal sets the horizontal alignment for the widget placed in the
-// container. Has no effect if the container contains no widget.
-// Defaults to alignment in the center.
-func AlignHorizontal(h align.Horizontal) Option {
- return option(func(c *Container) error {
- c.opts.hAlign = h
- return nil
- })
-}
-
-// AlignVertical sets the vertical alignment for the widget placed in the container.
-// Has no effect if the container contains no widget.
-// Defaults to alignment in the middle.
-func AlignVertical(v align.Vertical) Option {
- return option(func(c *Container) error {
- c.opts.vAlign = v
- return nil
- })
-}
-
-// Border configures the container to have a border of the specified style.
-func Border(ls linestyle.LineStyle) Option {
- return option(func(c *Container) error {
- c.opts.border = ls
- return nil
- })
-}
-
-// BorderTitle sets a text title within the border.
-func BorderTitle(title string) Option {
- return option(func(c *Container) error {
- c.opts.borderTitle = title
- return nil
- })
-}
-
-// BorderTitleAlignLeft aligns the border title on the left.
-func BorderTitleAlignLeft() Option {
- return option(func(c *Container) error {
- c.opts.borderTitleHAlign = align.HorizontalLeft
- return nil
- })
-}
-
-// BorderTitleAlignCenter aligns the border title in the center.
-func BorderTitleAlignCenter() Option {
- return option(func(c *Container) error {
- c.opts.borderTitleHAlign = align.HorizontalCenter
- return nil
- })
-}
-
-// BorderTitleAlignRight aligns the border title on the right.
-func BorderTitleAlignRight() Option {
- return option(func(c *Container) error {
- c.opts.borderTitleHAlign = align.HorizontalRight
- return nil
- })
-}
-
-// BorderColor sets the color of the border around the container.
-// This option is inherited to sub containers created by container splits.
-func BorderColor(color cell.Color) Option {
- return option(func(c *Container) error {
- c.opts.inherited.borderColor = color
- return nil
- })
-}
-
-// FocusedColor sets the color of the border around the container when it has
-// keyboard focus.
-// This option is inherited to sub containers created by container splits.
-func FocusedColor(color cell.Color) Option {
- return option(func(c *Container) error {
- c.opts.inherited.focusedColor = color
- return nil
- })
-}
-
-// splitType identifies how a container is split.
-type splitType int
-
-// String implements fmt.Stringer()
-func (st splitType) String() string {
- if n, ok := splitTypeNames[st]; ok {
- return n
- }
- return "splitTypeUnknown"
-}
-
-// splitTypeNames maps splitType values to human readable names.
-var splitTypeNames = map[splitType]string{
- splitTypeVertical: "splitTypeVertical",
- splitTypeHorizontal: "splitTypeHorizontal",
-}
-
-const (
- splitTypeVertical splitType = iota
- splitTypeHorizontal
-)
-
-// LeftOption is used to provide options to the left sub container after a
-// vertical split of the parent.
-type LeftOption interface {
- // lOpts returns the options.
- lOpts() []Option
-}
-
-// leftOption implements LeftOption.
-type leftOption func() []Option
-
-// lOpts implements LeftOption.lOpts.
-func (lo leftOption) lOpts() []Option {
- if lo == nil {
- return nil
- }
- return lo()
-}
-
-// Left applies options to the left sub container after a vertical split of the parent.
-func Left(opts ...Option) LeftOption {
- return leftOption(func() []Option {
- return opts
- })
-}
-
-// RightOption is used to provide options to the right sub container after a
-// vertical split of the parent.
-type RightOption interface {
- // rOpts returns the options.
- rOpts() []Option
-}
-
-// rightOption implements RightOption.
-type rightOption func() []Option
-
-// rOpts implements RightOption.rOpts.
-func (lo rightOption) rOpts() []Option {
- if lo == nil {
- return nil
- }
- return lo()
-}
-
-// Right applies options to the right sub container after a vertical split of the parent.
-func Right(opts ...Option) RightOption {
- return rightOption(func() []Option {
- return opts
- })
-}
-
-// TopOption is used to provide options to the top sub container after a
-// horizontal split of the parent.
-type TopOption interface {
- // tOpts returns the options.
- tOpts() []Option
-}
-
-// topOption implements TopOption.
-type topOption func() []Option
-
-// tOpts implements TopOption.tOpts.
-func (lo topOption) tOpts() []Option {
- if lo == nil {
- return nil
- }
- return lo()
-}
-
-// Top applies options to the top sub container after a horizontal split of the parent.
-func Top(opts ...Option) TopOption {
- return topOption(func() []Option {
- return opts
- })
-}
-
-// BottomOption is used to provide options to the bottom sub container after a
-// horizontal split of the parent.
-type BottomOption interface {
- // bOpts returns the options.
- bOpts() []Option
-}
-
-// bottomOption implements BottomOption.
-type bottomOption func() []Option
-
-// bOpts implements BottomOption.bOpts.
-func (lo bottomOption) bOpts() []Option {
- if lo == nil {
- return nil
- }
- return lo()
-}
-
-// Bottom applies options to the bottom sub container after a horizontal split of the parent.
-func Bottom(opts ...Option) BottomOption {
- return bottomOption(func() []Option {
- return opts
- })
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/container/traversal.go b/examples/go-dashboard/src/github.com/mum4k/termdash/container/traversal.go
deleted file mode 100644
index f728b50ba..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/container/traversal.go
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package container
-
-import (
- "errors"
- "fmt"
-)
-
-// traversal.go provides functions that navigate the container tree.
-
-// rootCont returns the root container.
-func rootCont(c *Container) *Container {
- for p := c.parent; p != nil; p = c.parent {
- c = p
- }
- return c
-}
-
-// visitFunc is executed during traversals when node is visited.
-// If the visit function returns an error, the traversal terminates and the
-// errStr is set to the text of the returned error.
-type visitFunc func(*Container) error
-
-// preOrder performs pre-order DFS traversal on the container tree.
-func preOrder(c *Container, errStr *string, visit visitFunc) {
- if c == nil || *errStr != "" {
- return
- }
-
- if err := visit(c); err != nil {
- *errStr = err.Error()
- return
- }
- preOrder(c.first, errStr, visit)
- preOrder(c.second, errStr, visit)
-}
-
-// postOrder performs post-order DFS traversal on the container tree.
-func postOrder(c *Container, errStr *string, visit visitFunc) {
- if c == nil || *errStr != "" {
- return
- }
-
- postOrder(c.first, errStr, visit)
- postOrder(c.second, errStr, visit)
- if err := visit(c); err != nil {
- *errStr = err.Error()
- return
- }
-}
-
-// findID finds container with the provided ID.
-// Returns an error of there is no container with the specified ID.
-func findID(root *Container, id string) (*Container, error) {
- if id == "" {
- return nil, errors.New("the container ID must not be empty")
- }
-
- var (
- errStr string
- cont *Container
- )
- preOrder(root, &errStr, visitFunc(func(c *Container) error {
- if c.opts.id == id {
- cont = c
- }
- return nil
- }))
- if cont == nil {
- return nil, fmt.Errorf("cannot find container with ID %q", id)
- }
- return cont, nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/go.mod b/examples/go-dashboard/src/github.com/mum4k/termdash/go.mod
deleted file mode 100644
index 3a81b3235..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/go.mod
+++ /dev/null
@@ -1,10 +0,0 @@
-module github.com/mum4k/termdash
-
-go 1.14
-
-require (
- github.com/gdamore/tcell v1.3.0
- github.com/kylelemons/godebug v1.1.0
- github.com/mattn/go-runewidth v0.0.9
- github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be
-)
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/keyboard/keyboard.go b/examples/go-dashboard/src/github.com/mum4k/termdash/keyboard/keyboard.go
deleted file mode 100644
index 3a852b326..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/keyboard/keyboard.go
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package keyboard defines well known keyboard keys and shortcuts.
-package keyboard
-
-// Key represents a single button on the keyboard.
-// Printable characters are set to their ASCII/Unicode rune value.
-// Non-printable (control) characters are equal to one of the constants defined
-// below.
-type Key rune
-
-// String implements fmt.Stringer()
-func (b Key) String() string {
- if n, ok := buttonNames[b]; ok {
- return n
- } else if b >= 0 {
- return string(b)
- }
- return "KeyUnknown"
-}
-
-// buttonNames maps Key values to human readable names.
-var buttonNames = map[Key]string{
- KeyF1: "KeyF1",
- KeyF2: "KeyF2",
- KeyF3: "KeyF3",
- KeyF4: "KeyF4",
- KeyF5: "KeyF5",
- KeyF6: "KeyF6",
- KeyF7: "KeyF7",
- KeyF8: "KeyF8",
- KeyF9: "KeyF9",
- KeyF10: "KeyF10",
- KeyF11: "KeyF11",
- KeyF12: "KeyF12",
- KeyInsert: "KeyInsert",
- KeyDelete: "KeyDelete",
- KeyHome: "KeyHome",
- KeyEnd: "KeyEnd",
- KeyPgUp: "KeyPgUp",
- KeyPgDn: "KeyPgDn",
- KeyArrowUp: "KeyArrowUp",
- KeyArrowDown: "KeyArrowDown",
- KeyArrowLeft: "KeyArrowLeft",
- KeyArrowRight: "KeyArrowRight",
- KeyCtrlTilde: "KeyCtrlTilde",
- KeyCtrlA: "KeyCtrlA",
- KeyCtrlB: "KeyCtrlB",
- KeyCtrlC: "KeyCtrlC",
- KeyCtrlD: "KeyCtrlD",
- KeyCtrlE: "KeyCtrlE",
- KeyCtrlF: "KeyCtrlF",
- KeyCtrlG: "KeyCtrlG",
- KeyBackspace: "KeyBackspace",
- KeyTab: "KeyTab",
- KeyCtrlJ: "KeyCtrlJ",
- KeyCtrlK: "KeyCtrlK",
- KeyCtrlL: "KeyCtrlL",
- KeyEnter: "KeyEnter",
- KeyCtrlN: "KeyCtrlN",
- KeyCtrlO: "KeyCtrlO",
- KeyCtrlP: "KeyCtrlP",
- KeyCtrlQ: "KeyCtrlQ",
- KeyCtrlR: "KeyCtrlR",
- KeyCtrlS: "KeyCtrlS",
- KeyCtrlT: "KeyCtrlT",
- KeyCtrlU: "KeyCtrlU",
- KeyCtrlV: "KeyCtrlV",
- KeyCtrlW: "KeyCtrlW",
- KeyCtrlX: "KeyCtrlX",
- KeyCtrlY: "KeyCtrlY",
- KeyCtrlZ: "KeyCtrlZ",
- KeyEsc: "KeyEsc",
- KeyCtrl4: "KeyCtrl4",
- KeyCtrl5: "KeyCtrl5",
- KeyCtrl6: "KeyCtrl6",
- KeyCtrl7: "KeyCtrl7",
- KeySpace: "KeySpace",
- KeyBackspace2: "KeyBackspace2",
-}
-
-// Printable characters, but worth having constants for them.
-const (
- KeySpace = ' '
-)
-
-// Negative values for non-printable characters.
-const (
- KeyF1 Key = -(iota + 1)
- KeyF2
- KeyF3
- KeyF4
- KeyF5
- KeyF6
- KeyF7
- KeyF8
- KeyF9
- KeyF10
- KeyF11
- KeyF12
- KeyInsert
- KeyDelete
- KeyHome
- KeyEnd
- KeyPgUp
- KeyPgDn
- KeyArrowUp
- KeyArrowDown
- KeyArrowLeft
- KeyArrowRight
- KeyCtrlTilde
- KeyCtrlA
- KeyCtrlB
- KeyCtrlC
- KeyCtrlD
- KeyCtrlE
- KeyCtrlF
- KeyCtrlG
- KeyBackspace
- KeyTab
- KeyCtrlJ
- KeyCtrlK
- KeyCtrlL
- KeyEnter
- KeyCtrlN
- KeyCtrlO
- KeyCtrlP
- KeyCtrlQ
- KeyCtrlR
- KeyCtrlS
- KeyCtrlT
- KeyCtrlU
- KeyCtrlV
- KeyCtrlW
- KeyCtrlX
- KeyCtrlY
- KeyCtrlZ
- KeyEsc
- KeyCtrl4
- KeyCtrl5
- KeyCtrl6
- KeyCtrl7
- KeyBackspace2
-)
-
-// Keys declared as duplicates by termbox.
-const (
- KeyCtrl2 Key = KeyCtrlTilde
- KeyCtrlSpace Key = KeyCtrlTilde
- KeyCtrlH Key = KeyBackspace
- KeyCtrlI Key = KeyTab
- KeyCtrlM Key = KeyEnter
- KeyCtrlLsqBracket Key = KeyEsc
- KeyCtrl3 Key = KeyEsc
- KeyCtrlBackslash Key = KeyCtrl4
- KeyCtrlRsqBracket Key = KeyCtrl5
- KeyCtrlSlash Key = KeyCtrl7
- KeyCtrlUnderscore Key = KeyCtrl7
- KeyCtrl8 Key = KeyBackspace2
-)
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/linestyle/linestyle.go b/examples/go-dashboard/src/github.com/mum4k/termdash/linestyle/linestyle.go
deleted file mode 100644
index c34fc3959..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/linestyle/linestyle.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package linestyle defines various line styles.
-package linestyle
-
-// LineStyle defines the supported line styles.
-type LineStyle int
-
-// String implements fmt.Stringer()
-func (ls LineStyle) String() string {
- if n, ok := lineStyleNames[ls]; ok {
- return n
- }
- return "LineStyleUnknown"
-}
-
-// lineStyleNames maps LineStyle values to human readable names.
-var lineStyleNames = map[LineStyle]string{
- None: "LineStyleNone",
- Light: "LineStyleLight",
- Double: "LineStyleDouble",
- Round: "LineStyleRound",
-}
-
-// Supported line styles.
-// See https://en.wikipedia.org/wiki/Box-drawing_character.
-const (
- // None indicates that no line should be present.
- None LineStyle = iota
-
- // Light is line style using the '─' characters.
- Light
-
- // Double is line style using the '═' characters.
- Double
-
- // Round is line style using the rounded corners '╭' characters.
- Round
-)
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/mouse/mouse.go b/examples/go-dashboard/src/github.com/mum4k/termdash/mouse/mouse.go
deleted file mode 100644
index d21e1d310..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/mouse/mouse.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package mouse defines known mouse buttons.
-package mouse
-
-// Button represents a mouse button.
-type Button int
-
-// String implements fmt.Stringer()
-func (b Button) String() string {
- if n, ok := buttonNames[b]; ok {
- return n
- }
- return "ButtonUnknown"
-}
-
-// buttonNames maps Button values to human readable names.
-var buttonNames = map[Button]string{
- ButtonLeft: "ButtonLeft",
- ButtonRight: "ButtonRight",
- ButtonMiddle: "ButtonMiddle",
- ButtonRelease: "ButtonRelease",
- ButtonWheelUp: "ButtonWheelUp",
- ButtonWheelDown: "ButtonWheelDown",
-}
-
-// Buttons recognized on the mouse.
-const (
- buttonUnknown Button = iota
- ButtonLeft
- ButtonRight
- ButtonMiddle
- ButtonRelease
- ButtonWheelUp
- ButtonWheelDown
-)
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/alignfor/alignfor.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/alignfor/alignfor.go
deleted file mode 100644
index 93cbac844..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/alignfor/alignfor.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package alignfor provides functions that align elements.
-package alignfor
-
-import (
- "fmt"
- "image"
- "strings"
-
- "github.com/mum4k/termdash/align"
- "github.com/mum4k/termdash/private/runewidth"
- "github.com/mum4k/termdash/private/wrap"
-)
-
-// hAlign aligns the given area in the rectangle horizontally.
-func hAlign(rect image.Rectangle, ar image.Rectangle, h align.Horizontal) (image.Rectangle, error) {
- gap := rect.Dx() - ar.Dx()
- switch h {
- case align.HorizontalRight:
- // Use gap from above.
- case align.HorizontalCenter:
- gap /= 2
- case align.HorizontalLeft:
- gap = 0
- default:
- return image.ZR, fmt.Errorf("unsupported horizontal alignment %v", h)
- }
-
- return image.Rect(
- rect.Min.X+gap,
- ar.Min.Y,
- rect.Min.X+gap+ar.Dx(),
- ar.Max.Y,
- ), nil
-}
-
-// vAlign aligns the given area in the rectangle vertically.
-func vAlign(rect image.Rectangle, ar image.Rectangle, v align.Vertical) (image.Rectangle, error) {
- gap := rect.Dy() - ar.Dy()
- switch v {
- case align.VerticalBottom:
- // Use gap from above.
- case align.VerticalMiddle:
- gap /= 2
- case align.VerticalTop:
- gap = 0
- default:
- return image.ZR, fmt.Errorf("unsupported vertical alignment %v", v)
- }
-
- return image.Rect(
- ar.Min.X,
- rect.Min.Y+gap,
- ar.Max.X,
- rect.Min.Y+gap+ar.Dy(),
- ), nil
-}
-
-// Rectangle aligns the area within the rectangle returning the
-// aligned area. The area must fall within the rectangle.
-func Rectangle(rect image.Rectangle, ar image.Rectangle, h align.Horizontal, v align.Vertical) (image.Rectangle, error) {
- if !ar.In(rect) {
- return image.ZR, fmt.Errorf("cannot align area %v inside rectangle %v, the area falls outside of the rectangle", ar, rect)
- }
-
- aligned, err := hAlign(rect, ar, h)
- if err != nil {
- return image.ZR, err
- }
- aligned, err = vAlign(rect, aligned, v)
- if err != nil {
- return image.ZR, err
- }
- return aligned, nil
-}
-
-// Text aligns the text within the given rectangle, returns the start point for the text.
-// For the purposes of the alignment this assumes that text will be trimmed if
-// it overruns the rectangle.
-// This only supports a single line of text, the text must not contain non-printable characters,
-// allows empty text.
-func Text(rect image.Rectangle, text string, h align.Horizontal, v align.Vertical) (image.Point, error) {
- if strings.ContainsRune(text, '\n') {
- return image.ZP, fmt.Errorf("the provided text contains a newline character: %q", text)
- }
-
- if text != "" {
- if err := wrap.ValidText(text); err != nil {
- return image.ZP, fmt.Errorf("the provided text contains non printable character(s): %s", err)
- }
- }
-
- cells := runewidth.StringWidth(text)
- var textLen int
- if cells < rect.Dx() {
- textLen = cells
- } else {
- textLen = rect.Dx()
- }
-
- textRect := image.Rect(
- rect.Min.X,
- rect.Min.Y,
- // For the purposes of aligning the text, assume that it will be
- // trimmed to the available space.
- rect.Min.X+textLen,
- rect.Min.Y+1,
- )
-
- aligned, err := Rectangle(rect, textRect, h, v)
- if err != nil {
- return image.ZP, err
- }
- return image.Point{aligned.Min.X, aligned.Min.Y}, nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/area/area.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/area/area.go
deleted file mode 100644
index 34b21a1b5..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/area/area.go
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package area provides functions working with image areas.
-package area
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/private/numbers"
-)
-
-// Size returns the size of the provided area.
-func Size(area image.Rectangle) image.Point {
- return image.Point{
- area.Dx(),
- area.Dy(),
- }
-}
-
-// FromSize returns the corresponding area for the provided size.
-func FromSize(size image.Point) (image.Rectangle, error) {
- if size.X < 0 || size.Y < 0 {
- return image.Rectangle{}, fmt.Errorf("cannot convert zero or negative size to an area, got: %+v", size)
- }
- return image.Rect(0, 0, size.X, size.Y), nil
-}
-
-// HSplit returns two new areas created by splitting the provided area at the
-// specified percentage of its width. The percentage must be in the range
-// 0 <= heightPerc <= 100.
-// Can return zero size areas.
-func HSplit(area image.Rectangle, heightPerc int) (top image.Rectangle, bottom image.Rectangle, err error) {
- if min, max := 0, 100; heightPerc < min || heightPerc > max {
- return image.ZR, image.ZR, fmt.Errorf("invalid heightPerc %d, must be in range %d <= heightPerc <= %d", heightPerc, min, max)
- }
- height := area.Dy() * heightPerc / 100
- top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+height)
- if top.Dy() == 0 {
- top = image.ZR
- }
- bottom = image.Rect(area.Min.X, area.Min.Y+height, area.Max.X, area.Max.Y)
- if bottom.Dy() == 0 {
- bottom = image.ZR
- }
- return top, bottom, nil
-}
-
-// VSplit returns two new areas created by splitting the provided area at the
-// specified percentage of its width. The percentage must be in the range
-// 0 <= widthPerc <= 100.
-// Can return zero size areas.
-func VSplit(area image.Rectangle, widthPerc int) (left image.Rectangle, right image.Rectangle, err error) {
- if min, max := 0, 100; widthPerc < min || widthPerc > max {
- return image.ZR, image.ZR, fmt.Errorf("invalid widthPerc %d, must be in range %d <= widthPerc <= %d", widthPerc, min, max)
- }
- width := area.Dx() * widthPerc / 100
- left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+width, area.Max.Y)
- if left.Dx() == 0 {
- left = image.ZR
- }
- right = image.Rect(area.Min.X+width, area.Min.Y, area.Max.X, area.Max.Y)
- if right.Dx() == 0 {
- right = image.ZR
- }
- return left, right, nil
-}
-
-// VSplitCells returns two new areas created by splitting the provided area
-// after the specified amount of cells of its width. The number of cells must
-// be a zero or a positive integer. Providing a zero returns left=image.ZR,
-// right=area. Providing a number equal or larger to area's width returns
-// left=area, right=image.ZR.
-func VSplitCells(area image.Rectangle, cells int) (left image.Rectangle, right image.Rectangle, err error) {
- if min := 0; cells < min {
- return image.ZR, image.ZR, fmt.Errorf("invalid cells %d, must be a positive integer", cells)
- }
- if cells == 0 {
- return image.ZR, area, nil
- }
-
- width := area.Dx()
- if cells >= width {
- return area, image.ZR, nil
- }
-
- left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+cells, area.Max.Y)
- right = image.Rect(area.Min.X+cells, area.Min.Y, area.Max.X, area.Max.Y)
- return left, right, nil
-}
-
-// HSplitCells returns two new areas created by splitting the provided area
-// after the specified amount of cells of its height. The number of cells must
-// be a zero or a positive integer. Providing a zero returns top=image.ZR,
-// bottom=area. Providing a number equal or larger to area's height returns
-// top=area, bottom=image.ZR.
-func HSplitCells(area image.Rectangle, cells int) (top image.Rectangle, bottom image.Rectangle, err error) {
- if min := 0; cells < min {
- return image.ZR, image.ZR, fmt.Errorf("invalid cells %d, must be a positive integer", cells)
- }
- if cells == 0 {
- return image.ZR, area, nil
- }
-
- height := area.Dy()
- if cells >= height {
- return area, image.ZR, nil
- }
-
- top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+cells)
- bottom = image.Rect(area.Min.X, area.Min.Y+cells, area.Max.X, area.Max.Y)
- return top, bottom, nil
-}
-
-// ExcludeBorder returns a new area created by subtracting a border around the
-// provided area. Return the zero area if there isn't enough space to exclude
-// the border.
-func ExcludeBorder(area image.Rectangle) image.Rectangle {
- // If the area dimensions are smaller than this, subtracting a point for the
- // border on each of its sides results in a zero area.
- const minDim = 2
- if area.Dx() < minDim || area.Dy() < minDim {
- return image.ZR
- }
- return image.Rect(
- numbers.Abs(area.Min.X+1),
- numbers.Abs(area.Min.Y+1),
- numbers.Abs(area.Max.X-1),
- numbers.Abs(area.Max.Y-1),
- )
-}
-
-// WithRatio returns the largest area that has the requested ratio but is
-// either equal or smaller than the provided area. Returns zero area if the
-// area or the ratio are zero, or if there is no such area.
-func WithRatio(area image.Rectangle, ratio image.Point) image.Rectangle {
- ratio = numbers.SimplifyRatio(ratio)
- if area == image.ZR || ratio == image.ZP {
- return image.ZR
- }
-
- wFact := area.Dx() / ratio.X
- hFact := area.Dy() / ratio.Y
-
- var fact int
- if wFact < hFact {
- fact = wFact
- } else {
- fact = hFact
- }
- return image.Rect(
- area.Min.X,
- area.Min.Y,
- ratio.X*fact+area.Min.X,
- ratio.Y*fact+area.Min.Y,
- )
-}
-
-// Shrink returns a new area whose size is reduced by the specified amount of
-// cells. Can return a zero area if there is no space left in the area.
-// The values must be zero or positive integers.
-func Shrink(area image.Rectangle, topCells, rightCells, bottomCells, leftCells int) (image.Rectangle, error) {
- for _, v := range []struct {
- name string
- value int
- }{
- {"topCells", topCells},
- {"rightCells", rightCells},
- {"bottomCells", bottomCells},
- {"leftCells", leftCells},
- } {
- if min := 0; v.value < min {
- return image.ZR, fmt.Errorf("invalid %s(%d), must be in range %d <= value", v.name, v.value, min)
- }
- }
-
- shrunk := area
- shrunk.Min.X, _ = numbers.MinMaxInts([]int{shrunk.Min.X + leftCells, shrunk.Max.X})
- _, shrunk.Max.X = numbers.MinMaxInts([]int{shrunk.Max.X - rightCells, shrunk.Min.X})
- shrunk.Min.Y, _ = numbers.MinMaxInts([]int{shrunk.Min.Y + topCells, shrunk.Max.Y})
- _, shrunk.Max.Y = numbers.MinMaxInts([]int{shrunk.Max.Y - bottomCells, shrunk.Min.Y})
-
- if shrunk.Dx() == 0 || shrunk.Dy() == 0 {
- return image.ZR, nil
- }
- return shrunk, nil
-}
-
-// ShrinkPercent returns a new area whose size is reduced by percentage of its
-// width or height. Can return a zero area if there is no space left in the area.
-// The topPerc and bottomPerc indicate the percentage of area's height.
-// The rightPerc and leftPerc indicate the percentage of area's width.
-// The percentages must be in range 0 <= v <= 100.
-func ShrinkPercent(area image.Rectangle, topPerc, rightPerc, bottomPerc, leftPerc int) (image.Rectangle, error) {
- for _, v := range []struct {
- name string
- value int
- }{
- {"topPerc", topPerc},
- {"rightPerc", rightPerc},
- {"bottomPerc", bottomPerc},
- {"leftPerc", leftPerc},
- } {
- if min, max := 0, 100; v.value < min || v.value > max {
- return image.ZR, fmt.Errorf("invalid %s(%d), must be in range %d <= value <= %d", v.name, v.value, min, max)
- }
- }
-
- top := area.Dy() * topPerc / 100
- bottom := area.Dy() * bottomPerc / 100
- right := area.Dx() * rightPerc / 100
- left := area.Dx() * leftPerc / 100
- return Shrink(area, top, right, bottom, left)
-}
-
-// MoveUp returns a new area that is moved up by the specified amount of cells.
-// Returns an error if the move would result in negative Y coordinates.
-// The values must be zero or positive integers.
-func MoveUp(area image.Rectangle, cells int) (image.Rectangle, error) {
- if min := 0; cells < min {
- return image.ZR, fmt.Errorf("cannot move area %v up by %d cells, must be in range %d <= value", area, cells, min)
- }
-
- if area.Min.Y < cells {
- return image.ZR, fmt.Errorf("cannot move area %v up by %d cells, would result in negative Y coordinate", area, cells)
- }
-
- moved := area
- moved.Min.Y -= cells
- moved.Max.Y -= cells
- return moved, nil
-}
-
-// MoveDown returns a new area that is moved down by the specified amount of
-// cells.
-// The values must be zero or positive integers.
-func MoveDown(area image.Rectangle, cells int) (image.Rectangle, error) {
- if min := 0; cells < min {
- return image.ZR, fmt.Errorf("cannot move area %v down by %d cells, must be in range %d <= value", area, cells, min)
- }
-
- moved := area
- moved.Min.Y += cells
- moved.Max.Y += cells
- return moved, nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/button/button.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/button/button.go
deleted file mode 100644
index d4e0601b8..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/button/button.go
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package button implements a state machine that tracks mouse button clicks.
-package button
-
-import (
- "image"
-
- "github.com/mum4k/termdash/mouse"
- "github.com/mum4k/termdash/terminal/terminalapi"
-)
-
-// State represents the state of the mouse button.
-type State int
-
-// String implements fmt.Stringer()
-func (s State) String() string {
- if n, ok := stateNames[s]; ok {
- return n
- }
- return "StateUnknown"
-}
-
-// stateNames maps State values to human readable names.
-var stateNames = map[State]string{
- Up: "StateUp",
- Down: "StateDown",
-}
-
-const (
- // Up is the default idle state of the mouse button.
- Up State = iota
-
- // Down is a state where the mouse button is pressed down and held.
- Down
-)
-
-// FSM implements a finite-state machine that tracks mouse clicks within an
-// area.
-//
-// Simplifies tracking of mouse button clicks, i.e. when the caller wants to
-// perform an action only if both the button press and release happen within
-// the specified area.
-//
-// This object is not thread-safe.
-type FSM struct {
- // button is the mouse button whose state this FSM tracks.
- button mouse.Button
-
- // area is the area provided to NewFSM.
- area image.Rectangle
-
- // state is the current state of the FSM.
- state stateFn
-}
-
-// NewFSM creates a new FSM instance that tracks the state of the specified
-// mouse button through button events that fall within the provided area.
-func NewFSM(button mouse.Button, area image.Rectangle) *FSM {
- return &FSM{
- button: button,
- area: area,
- state: wantPress,
- }
-}
-
-// Event is used to forward mouse events to the state machine.
-// Only events related to the button specified on a call to NewFSM are
-// processed.
-//
-// Returns a bool indicating if an action guarded by the button should be
-// performed and the state of the button after the provided event.
-// The bool is true if the button click should take an effect, i.e. if the
-// FSM saw both the button click and its release.
-func (fsm *FSM) Event(m *terminalapi.Mouse) (bool, State) {
- clicked, bs, next := fsm.state(fsm, m)
- fsm.state = next
- return clicked, bs
-}
-
-// UpdateArea informs FSM of an area change.
-// This method is idempotent.
-func (fsm *FSM) UpdateArea(area image.Rectangle) {
- fsm.area = area
-}
-
-// stateFn is a single state in the state machine.
-// Returns bool indicating if a click happened, the state of the button and the
-// next state of the FSM.
-type stateFn func(fsm *FSM, m *terminalapi.Mouse) (bool, State, stateFn)
-
-// wantPress is the initial state, expecting a button press inside the area.
-func wantPress(fsm *FSM, m *terminalapi.Mouse) (bool, State, stateFn) {
- if m.Button != fsm.button || !m.Position.In(fsm.area) {
- return false, Up, wantPress
- }
- return false, Down, wantRelease
-}
-
-// wantRelease waits for a mouse button release in the same area as
-// the press.
-func wantRelease(fsm *FSM, m *terminalapi.Mouse) (bool, State, stateFn) {
- switch m.Button {
- case fsm.button:
- if m.Position.In(fsm.area) {
- // Remain in the same state, since termbox reports move of mouse with
- // button held down as a series of clicks, one per position.
- return false, Down, wantRelease
- }
- return false, Up, wantPress
-
- case mouse.ButtonRelease:
- if m.Position.In(fsm.area) {
- // Seen both press and release, report a click.
- return true, Up, wantPress
- }
- // Release the button even if the release event happened outside of the area.
- return false, Up, wantPress
-
- default:
- return false, Up, wantPress
- }
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/braille/braille.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/braille/braille.go
deleted file mode 100644
index 7cd902f87..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/braille/braille.go
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-/*
-Package braille provides a canvas that uses braille characters.
-
-This is inspired by https://github.com/asciimoo/drawille.
-
-The braille patterns documentation:
-http://www.alanwood.net/unicode/braille_patterns.html
-
-The use of braille characters gives additional points (higher resolution) on
-the canvas, each character cell now has eight pixels that can be set
-independently. Specifically each cell has the following pixels, the axes grow
-right and down.
-
-Each cell:
-
- X→ 0 1 Y
- ┌───┐ ↓
- │● ●│ 0
- │● ●│ 1
- │● ●│ 2
- │● ●│ 3
- └───┘
-
-When using the braille canvas, the coordinates address the sub-cell points
-rather then cells themselves. However all points in the cell still share the
-same cell options.
-*/
-package braille
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/canvas"
- "github.com/mum4k/termdash/terminal/terminalapi"
-)
-
-const (
- // ColMult is the resolution multiplier for the width, i.e. two pixels per cell.
- ColMult = 2
-
- // RowMult is the resolution multiplier for the height, i.e. four pixels per cell.
- RowMult = 4
-
- // brailleCharOffset is the offset of the braille pattern unicode characters.
- // From: http://www.alanwood.net/unicode/braille_patterns.html
- brailleCharOffset = 0x2800
-
- // brailleLastChar is the last braille pattern rune.
- brailleLastChar = 0x28FF
-)
-
-// pixelRunes maps points addressing individual pixels in a cell into character
-// offset. I.e. the correct character to set pixel(0,0) is
-// brailleCharOffset|pixelRunes[image.Point{0,0}].
-var pixelRunes = map[image.Point]rune{
- {0, 0}: 0x01, {1, 0}: 0x08,
- {0, 1}: 0x02, {1, 1}: 0x10,
- {0, 2}: 0x04, {1, 2}: 0x20,
- {0, 3}: 0x40, {1, 3}: 0x80,
-}
-
-// Canvas is a canvas that uses the braille patterns. It is two times wider
-// and four times taller than a regular canvas that uses just plain characters,
-// since each cell now has 2x4 pixels that can be independently set.
-//
-// The braille canvas is an abstraction built on top of a regular character
-// canvas. After setting and toggling pixels on the braille canvas, it should
-// be copied to a regular character canvas or applied to a terminal which
-// results in setting of braille pattern characters.
-// See the examples for more details.
-//
-// The created braille canvas can be smaller and even misaligned relatively to
-// the regular character canvas or terminal, allowing the callers to create a
-// "view" of just a portion of the canvas or terminal.
-type Canvas struct {
- // regular is the regular character canvas the braille canvas is based on.
- regular *canvas.Canvas
-}
-
-// New returns a new braille canvas for the provided area.
-func New(ar image.Rectangle) (*Canvas, error) {
- rc, err := canvas.New(ar)
- if err != nil {
- return nil, err
- }
- return &Canvas{
- regular: rc,
- }, nil
-}
-
-// Size returns the size of the braille canvas in pixels.
-func (c *Canvas) Size() image.Point {
- s := c.regular.Size()
- return image.Point{s.X * ColMult, s.Y * RowMult}
-}
-
-// CellArea returns the area of the underlying cell canvas in cells.
-func (c *Canvas) CellArea() image.Rectangle {
- return c.regular.Area()
-}
-
-// Area returns the area of the braille canvas in pixels.
-// This will be zero-based area that is two times wider and four times taller
-// than the area used to create the braille canvas.
-func (c *Canvas) Area() image.Rectangle {
- ar := c.regular.Area()
- return image.Rect(0, 0, ar.Dx()*ColMult, ar.Dy()*RowMult)
-}
-
-// Clear clears all the content on the canvas.
-func (c *Canvas) Clear() error {
- return c.regular.Clear()
-}
-
-// SetPixel turns on pixel at the specified point.
-// The provided cell options will be applied to the entire cell (all of its
-// pixels). This method is idempotent.
-func (c *Canvas) SetPixel(p image.Point, opts ...cell.Option) error {
- cp, err := c.cellPoint(p)
- if err != nil {
- return err
- }
- cell, err := c.regular.Cell(cp)
- if err != nil {
- return err
- }
-
- var r rune
- if isBraille(cell.Rune) {
- // If the cell already has a braille pattern rune, we will be adding
- // the pixel.
- r = cell.Rune
- } else {
- r = brailleCharOffset
- }
-
- r |= pixelRunes[pixelPoint(p)]
- if _, err := c.regular.SetCell(cp, r, opts...); err != nil {
- return err
- }
- return nil
-}
-
-// ClearPixel turns off pixel at the specified point.
-// The provided cell options will be applied to the entire cell (all of its
-// pixels). This method is idempotent.
-func (c *Canvas) ClearPixel(p image.Point, opts ...cell.Option) error {
- cp, err := c.cellPoint(p)
- if err != nil {
- return err
- }
- cell, err := c.regular.Cell(cp)
- if err != nil {
- return err
- }
-
- // Clear is idempotent.
- if !isBraille(cell.Rune) || !pixelSet(cell.Rune, p) {
- return nil
- }
-
- r := cell.Rune & ^pixelRunes[pixelPoint(p)]
- if _, err := c.regular.SetCell(cp, r, opts...); err != nil {
- return err
- }
- return nil
-}
-
-// TogglePixel toggles the state of the pixel at the specified point, i.e. it
-// either sets or clear it depending on its current state.
-// The provided cell options will be applied to the entire cell (all of its
-// pixels).
-func (c *Canvas) TogglePixel(p image.Point, opts ...cell.Option) error {
- cp, err := c.cellPoint(p)
- if err != nil {
- return err
- }
- curCell, err := c.regular.Cell(cp)
- if err != nil {
- return err
- }
-
- if isBraille(curCell.Rune) && pixelSet(curCell.Rune, p) {
- return c.ClearPixel(p, opts...)
- }
- return c.SetPixel(p, opts...)
-}
-
-// SetCellOpts sets options on the specified cell of the braille canvas without
-// modifying the content of the cell.
-// Sets the default cell options if no options are provided.
-// This method is idempotent.
-func (c *Canvas) SetCellOpts(cellPoint image.Point, opts ...cell.Option) error {
- curCell, err := c.regular.Cell(cellPoint)
- if err != nil {
- return err
- }
-
- if len(opts) == 0 {
- // Set the default options.
- opts = []cell.Option{
- cell.FgColor(cell.ColorDefault),
- cell.BgColor(cell.ColorDefault),
- }
- }
- if _, err := c.regular.SetCell(cellPoint, curCell.Rune, opts...); err != nil {
- return err
- }
- return nil
-}
-
-// SetAreaCellOpts is like SetCellOpts, but sets the specified options on all
-// the cells within the provided area.
-func (c *Canvas) SetAreaCellOpts(cellArea image.Rectangle, opts ...cell.Option) error {
- haveArea := c.regular.Area()
- if !cellArea.In(haveArea) {
- return fmt.Errorf("unable to set cell options in area %v, it must fit inside the available cell area is %v", cellArea, haveArea)
- }
- for col := cellArea.Min.X; col < cellArea.Max.X; col++ {
- for row := cellArea.Min.Y; row < cellArea.Max.Y; row++ {
- if err := c.SetCellOpts(image.Point{col, row}, opts...); err != nil {
- return err
- }
- }
- }
- return nil
-}
-
-// Apply applies the canvas to the corresponding area of the terminal.
-// Guarantees to stay within limits of the area the canvas was created with.
-func (c *Canvas) Apply(t terminalapi.Terminal) error {
- return c.regular.Apply(t)
-}
-
-// CopyTo copies the content of this canvas onto the destination canvas.
-// This canvas can have an offset when compared to the destination canvas, i.e.
-// the area of this canvas doesn't have to be zero-based.
-func (c *Canvas) CopyTo(dst *canvas.Canvas) error {
- return c.regular.CopyTo(dst)
-}
-
-// cellPoint determines the point (coordinate) of the character cell given
-// coordinates in pixels.
-func (c *Canvas) cellPoint(p image.Point) (image.Point, error) {
- if p.X < 0 || p.Y < 0 {
- return image.ZP, fmt.Errorf("pixels cannot have negative coordinates: %v", p)
- }
- cp := image.Point{p.X / ColMult, p.Y / RowMult}
- if ar := c.regular.Area(); !cp.In(ar) {
- return image.ZP, fmt.Errorf("pixel at%v would be in a character cell at%v which falls outside of the canvas area %v", p, cp, ar)
- }
- return cp, nil
-}
-
-// isBraille determines if the rune is a braille pattern rune.
-func isBraille(r rune) bool {
- return r >= brailleCharOffset && r <= brailleLastChar
-}
-
-// pixelSet returns true if the provided rune has the specified pixel set.
-func pixelSet(r rune, p image.Point) bool {
- return r&pixelRunes[pixelPoint(p)] > 0
-}
-
-// pixelPoint translates point within canvas to point within the target cell.
-func pixelPoint(p image.Point) image.Point {
- return image.Point{p.X % ColMult, p.Y % RowMult}
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/buffer/buffer.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/buffer/buffer.go
deleted file mode 100644
index 5c21dd0ba..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/buffer/buffer.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package buffer implements a 2-D buffer of cells.
-package buffer
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/area"
- "github.com/mum4k/termdash/private/runewidth"
-)
-
-// NewCells breaks the provided text into cells and applies the options.
-func NewCells(text string, opts ...cell.Option) []*Cell {
- var res []*Cell
- for _, r := range text {
- res = append(res, NewCell(r, opts...))
- }
- return res
-}
-
-// Cell represents a single cell on the terminal.
-type Cell struct {
- // Rune is the rune stored in the cell.
- Rune rune
-
- // Opts are the cell options.
- Opts *cell.Options
-}
-
-// String implements fmt.Stringer.
-func (c *Cell) String() string {
- return fmt.Sprintf("{%q}", c.Rune)
-}
-
-// NewCell returns a new cell.
-func NewCell(r rune, opts ...cell.Option) *Cell {
- return &Cell{
- Rune: r,
- Opts: cell.NewOptions(opts...),
- }
-}
-
-// Copy returns a copy the cell.
-func (c *Cell) Copy() *Cell {
- return &Cell{
- Rune: c.Rune,
- Opts: cell.NewOptions(c.Opts),
- }
-}
-
-// Apply applies the provided options to the cell.
-func (c *Cell) Apply(opts ...cell.Option) {
- for _, opt := range opts {
- opt.Set(c.Opts)
- }
-}
-
-// Buffer is a 2-D buffer of cells.
-// The axes increase right and down.
-// Uninitialized buffer is invalid, use New to create an instance.
-// Don't set cells directly, use the SetCell method instead which safely
-// handles limits and wide unicode characters.
-type Buffer [][]*Cell
-
-// New returns a new Buffer of the provided size.
-func New(size image.Point) (Buffer, error) {
- if size.X <= 0 {
- return nil, fmt.Errorf("invalid buffer width (size.X): %d, must be a positive number", size.X)
- }
- if size.Y <= 0 {
- return nil, fmt.Errorf("invalid buffer height (size.Y): %d, must be a positive number", size.Y)
- }
-
- b := make([][]*Cell, size.X)
- for col := range b {
- b[col] = make([]*Cell, size.Y)
- for row := range b[col] {
- b[col][row] = NewCell(0)
- }
- }
- return b, nil
-}
-
-// SetCell sets the rune of the specified cell in the buffer. Returns the
-// number of cells the rune occupies, wide runes can occupy multiple cells when
-// printed on the terminal. See http://www.unicode.org/reports/tr11/.
-// Use the options to specify which attributes to modify, if an attribute
-// option isn't specified, the attribute retains its previous value.
-func (b Buffer) SetCell(p image.Point, r rune, opts ...cell.Option) (int, error) {
- partial, err := b.IsPartial(p)
- if err != nil {
- return -1, err
- }
- if partial {
- return -1, fmt.Errorf("cannot set rune %q at point %v, it is a partial cell occupied by a wide rune in the previous cell", r, p)
- }
-
- remW, err := b.RemWidth(p)
- if err != nil {
- return -1, err
- }
- rw := runewidth.RuneWidth(r)
- if rw == 0 {
- // Even if the rune is invisible, like the zero-value rune, it still
- // occupies at least the target cell.
- rw = 1
- }
- if rw > remW {
- return -1, fmt.Errorf("cannot set rune %q of width %d at point %v, only have %d remaining cells at this line", r, rw, p, remW)
- }
-
- c := b[p.X][p.Y]
- c.Rune = r
- c.Apply(opts...)
- return rw, nil
-}
-
-// IsPartial returns true if the cell at the specified point holds a part of a
-// full width rune from a previous cell. See
-// http://www.unicode.org/reports/tr11/.
-func (b Buffer) IsPartial(p image.Point) (bool, error) {
- size := b.Size()
- ar, err := area.FromSize(size)
- if err != nil {
- return false, err
- }
-
- if !p.In(ar) {
- return false, fmt.Errorf("point %v falls outside of the area %v occupied by the buffer", p, ar)
- }
-
- if p.X == 0 && p.Y == 0 {
- return false, nil
- }
-
- prevP := image.Point{p.X - 1, p.Y}
- if prevP.X < 0 {
- prevP = image.Point{size.X - 1, p.Y - 1}
- }
-
- prevR := b[prevP.X][prevP.Y].Rune
- switch rw := runewidth.RuneWidth(prevR); rw {
- case 0, 1:
- return false, nil
- case 2:
- return true, nil
- default:
- return false, fmt.Errorf("buffer cell %v contains rune %q which has an unsupported rune with %d", prevP, prevR, rw)
- }
-}
-
-// RemWidth returns the remaining width (horizontal row of cells) available
-// from and inclusive of the specified point.
-func (b Buffer) RemWidth(p image.Point) (int, error) {
- size := b.Size()
- ar, err := area.FromSize(size)
- if err != nil {
- return -1, err
- }
-
- if !p.In(ar) {
- return -1, fmt.Errorf("point %v falls outside of the area %v occupied by the buffer", p, ar)
- }
- return size.X - p.X, nil
-}
-
-// Size returns the size of the buffer.
-func (b Buffer) Size() image.Point {
- return image.Point{
- len(b),
- len(b[0]),
- }
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/canvas.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/canvas.go
deleted file mode 100644
index 65a1e6963..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/canvas/canvas.go
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package canvas defines the canvas that the widgets draw on.
-package canvas
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/area"
- "github.com/mum4k/termdash/private/canvas/buffer"
- "github.com/mum4k/termdash/private/runewidth"
- "github.com/mum4k/termdash/terminal/terminalapi"
-)
-
-// Canvas is where a widget draws its output for display on the terminal.
-type Canvas struct {
- // area is the area the buffer was created for.
- // Contains absolute coordinates on the target terminal, while the buffer
- // contains relative zero-based coordinates for this canvas.
- area image.Rectangle
-
- // buffer is where the drawing happens.
- buffer buffer.Buffer
-}
-
-// New returns a new Canvas with a buffer for the provided area.
-func New(ar image.Rectangle) (*Canvas, error) {
- if ar.Min.X < 0 || ar.Min.Y < 0 || ar.Max.X < 0 || ar.Max.Y < 0 {
- return nil, fmt.Errorf("area cannot start or end on the negative axis, got: %+v", ar)
- }
-
- b, err := buffer.New(area.Size(ar))
- if err != nil {
- return nil, err
- }
- return &Canvas{
- area: ar,
- buffer: b,
- }, nil
-}
-
-// Size returns the size of the 2-D canvas.
-func (c *Canvas) Size() image.Point {
- return c.buffer.Size()
-}
-
-// Area returns the area of the 2-D canvas.
-func (c *Canvas) Area() image.Rectangle {
- s := c.buffer.Size()
- return image.Rect(0, 0, s.X, s.Y)
-}
-
-// Clear clears all the content on the canvas.
-func (c *Canvas) Clear() error {
- b, err := buffer.New(c.Size())
- if err != nil {
- return err
- }
- c.buffer = b
- return nil
-}
-
-// SetCell sets the rune of the specified cell on the canvas. Returns the
-// number of cells the rune occupies, wide runes can occupy multiple cells when
-// printed on the terminal. See http://www.unicode.org/reports/tr11/.
-// Use the options to specify which attributes to modify, if an attribute
-// option isn't specified, the attribute retains its previous value.
-func (c *Canvas) SetCell(p image.Point, r rune, opts ...cell.Option) (int, error) {
- return c.buffer.SetCell(p, r, opts...)
-}
-
-// Cell returns a copy of the specified cell.
-func (c *Canvas) Cell(p image.Point) (*buffer.Cell, error) {
- ar, err := area.FromSize(c.Size())
- if err != nil {
- return nil, err
- }
- if !p.In(ar) {
- return nil, fmt.Errorf("point %v falls outside of the area %v occupied by the canvas", p, ar)
- }
-
- return c.buffer[p.X][p.Y].Copy(), nil
-}
-
-// SetCellOpts sets options on the specified cell of the canvas without
-// modifying the content of the cell.
-// Sets the default cell options if no options are provided.
-// This method is idempotent.
-func (c *Canvas) SetCellOpts(p image.Point, opts ...cell.Option) error {
- curCell, err := c.Cell(p)
- if err != nil {
- return err
- }
-
- if len(opts) == 0 {
- // Set the default options.
- opts = []cell.Option{
- cell.FgColor(cell.ColorDefault),
- cell.BgColor(cell.ColorDefault),
- }
- }
- if _, err := c.SetCell(p, curCell.Rune, opts...); err != nil {
- return err
- }
- return nil
-}
-
-// SetAreaCells is like SetCell, but sets the specified rune and options on all
-// the cells within the provided area.
-// This method is idempotent.
-func (c *Canvas) SetAreaCells(cellArea image.Rectangle, r rune, opts ...cell.Option) error {
- haveArea := c.Area()
- if !cellArea.In(haveArea) {
- return fmt.Errorf("unable to set cell runes in area %v, it must fit inside the available cell area is %v", cellArea, haveArea)
- }
-
- rw := runewidth.RuneWidth(r)
- for row := cellArea.Min.Y; row < cellArea.Max.Y; row++ {
- for col := cellArea.Min.X; col < cellArea.Max.X; {
- p := image.Point{col, row}
- if col+rw > cellArea.Max.X {
- break
- }
- cells, err := c.SetCell(p, r, opts...)
- if err != nil {
- return err
- }
- col += cells
- }
- }
- return nil
-}
-
-// SetAreaCellOpts is like SetCellOpts, but sets the specified options on all
-// the cells within the provided area.
-func (c *Canvas) SetAreaCellOpts(cellArea image.Rectangle, opts ...cell.Option) error {
- haveArea := c.Area()
- if !cellArea.In(haveArea) {
- return fmt.Errorf("unable to set cell options in area %v, it must fit inside the available cell area is %v", cellArea, haveArea)
- }
- for col := cellArea.Min.X; col < cellArea.Max.X; col++ {
- for row := cellArea.Min.Y; row < cellArea.Max.Y; row++ {
- if err := c.SetCellOpts(image.Point{col, row}, opts...); err != nil {
- return err
- }
- }
- }
- return nil
-}
-
-// setCellFunc is a function that sets cell content on a terminal or a canvas.
-type setCellFunc func(image.Point, rune, ...cell.Option) error
-
-// copyTo is the internal implementation of code that copies the content of a
-// canvas. If a non zero offset is provided, all the copied points are offset by
-// this amount.
-// The dstSetCell function is called for every point in this canvas when
-// copying it to the destination.
-func (c *Canvas) copyTo(offset image.Point, dstSetCell setCellFunc) error {
- for col := range c.buffer {
- for row := range c.buffer[col] {
- partial, err := c.buffer.IsPartial(image.Point{col, row})
- if err != nil {
- return err
- }
- if partial {
- // Skip over partial cells, i.e. cells that follow a cell
- // containing a full-width rune. A full-width rune takes only
- // one cell in the buffer, but two on the terminal.
- // See http://www.unicode.org/reports/tr11/.
- continue
- }
- cell := c.buffer[col][row]
- p := image.Point{col, row}.Add(offset)
- if err := dstSetCell(p, cell.Rune, cell.Opts); err != nil {
- return fmt.Errorf("setCellFunc%v => error: %v", p, err)
- }
- }
- }
- return nil
-}
-
-// Apply applies the canvas to the corresponding area of the terminal.
-// Guarantees to stay within limits of the area the canvas was created with.
-func (c *Canvas) Apply(t terminalapi.Terminal) error {
- termArea, err := area.FromSize(t.Size())
- if err != nil {
- return err
- }
-
- bufArea, err := area.FromSize(c.buffer.Size())
- if err != nil {
- return err
- }
-
- if !bufArea.In(termArea) {
- return fmt.Errorf("the canvas area %+v doesn't fit onto the terminal %+v", bufArea, termArea)
- }
-
- // The image.Point{0, 0} of this canvas isn't always exactly at
- // image.Point{0, 0} on the terminal.
- // Depends on area assigned by the container.
- offset := c.area.Min
- return c.copyTo(offset, t.SetCell)
-}
-
-// CopyTo copies the content of this canvas onto the destination canvas.
-// This canvas can have an offset when compared to the destination canvas, i.e.
-// the area of this canvas doesn't have to be zero-based.
-func (c *Canvas) CopyTo(dst *Canvas) error {
- if !c.area.In(dst.Area()) {
- return fmt.Errorf("the canvas area %v doesn't fit or lie inside the destination canvas area %v", c.area, dst.Area())
- }
-
- fn := setCellFunc(func(p image.Point, r rune, opts ...cell.Option) error {
- if _, err := dst.SetCell(p, r, opts...); err != nil {
- return fmt.Errorf("dst.SetCell => %v", err)
- }
- return nil
- })
-
- // Neither of the two canvases (source and destination) have to be zero
- // based. Canvas is not zero based if it is positioned elsewhere, i.e.
- // providing a smaller view of another canvas.
- // E.g. a widget can assign a smaller portion of its canvas to a component
- // in order to restrict drawing of this component to a smaller area. To do
- // this it can create a sub-canvas. This sub-canvas can have a specific
- // starting position other than image.Point{0, 0} relative to the parent
- // canvas. Copying this sub-canvas back onto the parent accounts for this
- // offset.
- offset := c.area.Min
- return c.copyTo(offset, fn)
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/border.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/border.go
deleted file mode 100644
index a19ec096c..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/border.go
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// border.go contains code that draws borders.
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/align"
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/linestyle"
- "github.com/mum4k/termdash/private/alignfor"
- "github.com/mum4k/termdash/private/canvas"
-)
-
-// BorderOption is used to provide options to Border().
-type BorderOption interface {
- // set sets the provided option.
- set(*borderOptions)
-}
-
-// borderOptions stores the provided options.
-type borderOptions struct {
- cellOpts []cell.Option
- lineStyle linestyle.LineStyle
- title string
- titleOM OverrunMode
- titleCellOpts []cell.Option
- titleHAlign align.Horizontal
-}
-
-// borderOption implements BorderOption.
-type borderOption func(bOpts *borderOptions)
-
-// set implements BorderOption.set.
-func (bo borderOption) set(bOpts *borderOptions) {
- bo(bOpts)
-}
-
-// DefaultBorderLineStyle is the default value for the BorderLineStyle option.
-const DefaultBorderLineStyle = linestyle.Light
-
-// BorderLineStyle sets the style of the line used to draw the border.
-func BorderLineStyle(ls linestyle.LineStyle) BorderOption {
- return borderOption(func(bOpts *borderOptions) {
- bOpts.lineStyle = ls
- })
-}
-
-// BorderCellOpts sets options on the cells that create the border.
-func BorderCellOpts(opts ...cell.Option) BorderOption {
- return borderOption(func(bOpts *borderOptions) {
- bOpts.cellOpts = opts
- })
-}
-
-// BorderTitle sets a title for the border.
-func BorderTitle(title string, overrun OverrunMode, opts ...cell.Option) BorderOption {
- return borderOption(func(bOpts *borderOptions) {
- bOpts.title = title
- bOpts.titleOM = overrun
- bOpts.titleCellOpts = opts
- })
-}
-
-// BorderTitleAlign configures the horizontal alignment for the title.
-func BorderTitleAlign(h align.Horizontal) BorderOption {
- return borderOption(func(bOpts *borderOptions) {
- bOpts.titleHAlign = h
- })
-}
-
-// borderChar returns the correct border character from the parts for the use
-// at the specified point of the border. Returns -1 if no character should be at
-// this point.
-func borderChar(p image.Point, border image.Rectangle, parts map[linePart]rune) rune {
- switch {
- case p.X == border.Min.X && p.Y == border.Min.Y:
- return parts[topLeftCorner]
- case p.X == border.Max.X-1 && p.Y == border.Min.Y:
- return parts[topRightCorner]
- case p.X == border.Min.X && p.Y == border.Max.Y-1:
- return parts[bottomLeftCorner]
- case p.X == border.Max.X-1 && p.Y == border.Max.Y-1:
- return parts[bottomRightCorner]
- case p.X == border.Min.X || p.X == border.Max.X-1:
- return parts[vLine]
- case p.Y == border.Min.Y || p.Y == border.Max.Y-1:
- return parts[hLine]
- }
- return -1
-}
-
-// drawTitle draws a text title at the top of the border.
-func drawTitle(c *canvas.Canvas, border image.Rectangle, opt *borderOptions) error {
- // Don't attempt to draw the title if there isn't space for at least one rune.
- // The title must not overwrite any of the corner runes on the border so we
- // need the following minimum width.
- const minForTitle = 3
- if border.Dx() < minForTitle {
- return nil
- }
-
- available := image.Rect(
- border.Min.X+1, // One space for the top left corner char.
- border.Min.Y,
- border.Max.X-1, // One space for the top right corner char.
- border.Min.Y+1,
- )
- start, err := alignfor.Text(available, opt.title, opt.titleHAlign, align.VerticalTop)
- if err != nil {
- return err
- }
-
- return Text(
- c, opt.title, start,
- TextCellOpts(opt.titleCellOpts...),
- TextOverrunMode(opt.titleOM),
- TextMaxX(available.Max.X),
- )
-}
-
-// Border draws a border on the canvas.
-func Border(c *canvas.Canvas, border image.Rectangle, opts ...BorderOption) error {
- if ar := c.Area(); !border.In(ar) {
- return fmt.Errorf("the requested border %+v falls outside of the provided canvas %+v", border, ar)
- }
-
- const minSize = 2
- if border.Dx() < minSize || border.Dy() < minSize {
- return fmt.Errorf("the smallest supported border is %dx%d, got: %dx%d", minSize, minSize, border.Dx(), border.Dy())
- }
-
- opt := &borderOptions{
- lineStyle: DefaultBorderLineStyle,
- }
- for _, o := range opts {
- o.set(opt)
- }
-
- parts, err := lineParts(opt.lineStyle)
- if err != nil {
- return err
- }
-
- for col := border.Min.X; col < border.Max.X; col++ {
- for row := border.Min.Y; row < border.Max.Y; row++ {
- p := image.Point{col, row}
- r := borderChar(p, border, parts)
- if r == -1 {
- continue
- }
-
- cells, err := c.SetCell(p, r, opt.cellOpts...)
- if err != nil {
- return err
- }
- if cells != 1 {
- panic(fmt.Sprintf("invalid border rune %q, this rune occupies %d cells, border implementation only supports half-width runes that occupy exactly one cell", r, cells))
- }
- }
- }
-
- if opt.title != "" {
- return drawTitle(c, border, opt)
- }
- return nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_circle.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_circle.go
deleted file mode 100644
index d2b3b86bc..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_circle.go
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// braille_circle.go contains code that draws circles on a braille canvas.
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/canvas/braille"
- "github.com/mum4k/termdash/private/numbers/trig"
-)
-
-// BrailleCircleOption is used to provide options to BrailleCircle.
-type BrailleCircleOption interface {
- // set sets the provided option.
- set(*brailleCircleOptions)
-}
-
-// brailleCircleOptions stores the provided options.
-type brailleCircleOptions struct {
- cellOpts []cell.Option
- filled bool
- pixelChange braillePixelChange
-
- arcOnly bool
- startDegree int
- endDegree int
-}
-
-// newBrailleCircleOptions returns a new brailleCircleOptions instance.
-func newBrailleCircleOptions() *brailleCircleOptions {
- return &brailleCircleOptions{
- pixelChange: braillePixelChangeSet,
- }
-}
-
-// validate validates the provided options.
-func (opts *brailleCircleOptions) validate() error {
- if !opts.arcOnly {
- return nil
- }
-
- if opts.startDegree == opts.endDegree {
- return fmt.Errorf("invalid degree range, start %d and end %d cannot be equal", opts.startDegree, opts.endDegree)
- }
- return nil
-}
-
-// brailleCircleOption implements BrailleCircleOption.
-type brailleCircleOption func(*brailleCircleOptions)
-
-// set implements BrailleCircleOption.set.
-func (o brailleCircleOption) set(opts *brailleCircleOptions) {
- o(opts)
-}
-
-// BrailleCircleCellOpts sets options on the cells that contain the circle.
-// Cell options on a braille canvas can only be set on the entire cell, not per
-// pixel.
-func BrailleCircleCellOpts(cOpts ...cell.Option) BrailleCircleOption {
- return brailleCircleOption(func(opts *brailleCircleOptions) {
- opts.cellOpts = cOpts
- })
-}
-
-// BrailleCircleFilled indicates that the drawn circle should be filled.
-func BrailleCircleFilled() BrailleCircleOption {
- return brailleCircleOption(func(opts *brailleCircleOptions) {
- opts.filled = true
- })
-}
-
-// BrailleCircleArcOnly indicates that only a portion of the circle should be drawn.
-// The arc will be between the two provided angles in degrees.
-// Each angle must be in range 0 <= angle <= 360. Start and end must not be equal.
-// The zero angle is on the X axis, angles grow counter-clockwise.
-func BrailleCircleArcOnly(startDegree, endDegree int) BrailleCircleOption {
- return brailleCircleOption(func(opts *brailleCircleOptions) {
- opts.arcOnly = true
- opts.startDegree = startDegree
- opts.endDegree = endDegree
-
- })
-}
-
-// BrailleCircleClearPixels changes the behavior of BrailleCircle, so that it
-// clears the pixels belonging to the circle instead of setting them.
-// Useful in order to "erase" a circle from the canvas as opposed to drawing one.
-func BrailleCircleClearPixels() BrailleCircleOption {
- return brailleCircleOption(func(opts *brailleCircleOptions) {
- opts.pixelChange = braillePixelChangeClear
- })
-}
-
-// BrailleCircle draws an approximated circle with the specified mid point and radius.
-// The mid point must be a valid pixel within the canvas.
-// All the points that form the circle must fit into the canvas.
-// The smallest valid radius is two.
-func BrailleCircle(bc *braille.Canvas, mid image.Point, radius int, opts ...BrailleCircleOption) error {
- if ar := bc.Area(); !mid.In(ar) {
- return fmt.Errorf("unable to draw circle with mid point %v which is outside of the braille canvas area %v", mid, ar)
- }
- if min := 2; radius < min {
- return fmt.Errorf("unable to draw circle with radius %d, must be in range %d <= radius", radius, min)
- }
-
- opt := newBrailleCircleOptions()
- for _, o := range opts {
- o.set(opt)
- }
-
- if err := opt.validate(); err != nil {
- return err
- }
-
- points := circlePoints(mid, radius)
- if opt.arcOnly {
- f, err := trig.FilterByAngle(points, mid, opt.startDegree, opt.endDegree)
- if err != nil {
- return err
- }
- points = f
- if opt.filled && (opt.startDegree != 0 || opt.endDegree != 360) {
- points = append(points, openingPoints(mid, radius, opt)...)
- }
- }
- if err := drawPoints(bc, points, opt); err != nil {
- return fmt.Errorf("failed to draw circle with mid:%v, radius:%d, start:%d degrees, end:%d degrees: %v", mid, radius, opt.startDegree, opt.endDegree, err)
- }
- if opt.filled {
- return fillCircle(bc, points, mid, radius, opt)
- }
- return nil
-}
-
-// drawPoints draws the points onto the canvas.
-func drawPoints(bc *braille.Canvas, points []image.Point, opt *brailleCircleOptions) error {
- for _, p := range points {
- switch opt.pixelChange {
- case braillePixelChangeSet:
- if err := bc.SetPixel(p, opt.cellOpts...); err != nil {
- return fmt.Errorf("SetPixel => %v", err)
- }
- case braillePixelChangeClear:
- if err := bc.ClearPixel(p, opt.cellOpts...); err != nil {
- return fmt.Errorf("ClearPixel => %v", err)
- }
-
- }
- }
- return nil
-}
-
-// fillCircle fills a circle that consists of the provided point and has the
-// mid point and radius.
-func fillCircle(bc *braille.Canvas, points []image.Point, mid image.Point, radius int, opt *brailleCircleOptions) error {
- lineOpts := []BrailleLineOption{
- BrailleLineCellOpts(opt.cellOpts...),
- }
- fillOpts := []BrailleFillOption{
- BrailleFillCellOpts(opt.cellOpts...),
- }
- if opt.pixelChange == braillePixelChangeClear {
- lineOpts = append(lineOpts, BrailleLineClearPixels())
- fillOpts = append(fillOpts, BrailleFillClearPixels())
- }
-
- // Determine a fill point that should be inside of the circle sector.
- midA, err := trig.RangeMid(opt.startDegree, opt.endDegree)
- if err != nil {
- return err
- }
- fp := trig.CirclePointAtAngle(midA, mid, radius-1)
-
- // Ensure the fill point falls inside the circle.
- // If drawing a partial circle, it must also fall within points belonging
- // to the opening.
- // This might not be true if drawing a partial circle and the arc is very
- // small.
- shape := points
- if opt.arcOnly {
- startP := trig.CirclePointAtAngle(opt.startDegree, mid, radius-1)
- endP := trig.CirclePointAtAngle(opt.endDegree, mid, radius-1)
- shape = append(shape, startP, endP)
- }
- if trig.PointIsIn(fp, shape) {
- if err := BrailleFill(bc, fp, points, fillOpts...); err != nil {
- return err
- }
- if err := BrailleLine(bc, mid, fp, lineOpts...); err != nil {
- return err
- }
- }
- return nil
-}
-
-// openingPoints returns points on the lines from the mid point to the circle
-// opening when drawing an incomplete circle.
-func openingPoints(mid image.Point, radius int, opt *brailleCircleOptions) []image.Point {
- var points []image.Point
- startP := trig.CirclePointAtAngle(opt.startDegree, mid, radius)
- endP := trig.CirclePointAtAngle(opt.endDegree, mid, radius)
- points = append(points, brailleLinePoints(mid, startP)...)
- points = append(points, brailleLinePoints(mid, endP)...)
- return points
-}
-
-// circlePoints returns a list of points that represent a circle with
-// the specified mid point and radius.
-func circlePoints(mid image.Point, radius int) []image.Point {
- var points []image.Point
-
- // Bresenham algorithm.
- // https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
- x := radius
- y := 0
- dx := 1
- dy := 1
- diff := dx - (radius << 1) // Cheap multiplication by two.
-
- for x >= y {
- points = append(
- points,
- image.Point{mid.X + x, mid.Y + y},
- image.Point{mid.X + y, mid.Y + x},
- image.Point{mid.X - y, mid.Y + x},
- image.Point{mid.X - x, mid.Y + y},
- image.Point{mid.X - x, mid.Y - y},
- image.Point{mid.X - y, mid.Y - x},
- image.Point{mid.X + y, mid.Y - x},
- image.Point{mid.X + x, mid.Y - y},
- )
-
- if diff <= 0 {
- y++
- diff += dy
- dy += 2
- }
-
- if diff > 0 {
- x--
- dx += 2
- diff += dx - (radius << 1)
- }
-
- }
- return points
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_fill.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_fill.go
deleted file mode 100644
index 8bb311f1c..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_fill.go
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// braille_fill.go implements the flood-fill algorithm for filling shapes on the braille canvas.
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/canvas/braille"
-)
-
-// BrailleFillOption is used to provide options to BrailleFill.
-type BrailleFillOption interface {
- // set sets the provided option.
- set(*brailleFillOptions)
-}
-
-// brailleFillOptions stores the provided options.
-type brailleFillOptions struct {
- cellOpts []cell.Option
- pixelChange braillePixelChange
-}
-
-// newBrailleFillOptions returns a new brailleFillOptions instance.
-func newBrailleFillOptions() *brailleFillOptions {
- return &brailleFillOptions{
- pixelChange: braillePixelChangeSet,
- }
-}
-
-// brailleFillOption implements BrailleFillOption.
-type brailleFillOption func(*brailleFillOptions)
-
-// set implements BrailleFillOption.set.
-func (o brailleFillOption) set(opts *brailleFillOptions) {
- o(opts)
-}
-
-// BrailleFillCellOpts sets options on the cells that are set as part of
-// filling shapes.
-// Cell options on a braille canvas can only be set on the entire cell, not per
-// pixel.
-func BrailleFillCellOpts(cOpts ...cell.Option) BrailleFillOption {
- return brailleFillOption(func(opts *brailleFillOptions) {
- opts.cellOpts = cOpts
- })
-}
-
-// BrailleFillClearPixels changes the behavior of BrailleFill, so that it
-// clears the pixels instead of setting them.
-// Useful in order to "erase" the filled area as opposed to drawing one.
-func BrailleFillClearPixels() BrailleFillOption {
- return brailleFillOption(func(opts *brailleFillOptions) {
- opts.pixelChange = braillePixelChangeClear
- })
-}
-
-// BrailleFill fills the braille canvas starting at the specified point.
-// The function will not fill or cross over any points in the defined border.
-// The start point must be in the canvas.
-func BrailleFill(bc *braille.Canvas, start image.Point, border []image.Point, opts ...BrailleFillOption) error {
- if ar := bc.Area(); !start.In(ar) {
- return fmt.Errorf("unable to start filling canvas at point %v which is outside of the braille canvas area %v", start, ar)
- }
-
- opt := newBrailleFillOptions()
- for _, o := range opts {
- o.set(opt)
- }
-
- b := map[image.Point]struct{}{}
- for _, p := range border {
- b[p] = struct{}{}
- }
-
- v := newVisitable(bc.Area(), b)
- visitor := func(p image.Point) error {
- switch opt.pixelChange {
- case braillePixelChangeSet:
- return bc.SetPixel(p, opt.cellOpts...)
- case braillePixelChangeClear:
- return bc.ClearPixel(p, opt.cellOpts...)
- }
- return nil
- }
- return brailleDFS(v, start, visitor)
-}
-
-// visitable represents an area that can be visited.
-// It tracks nodes that are already visited.
-type visitable struct {
- area image.Rectangle
- visited map[image.Point]struct{}
-}
-
-// newVisitable returns a new visitable object initialized for the provided
-// area and already visited nodes.
-func newVisitable(ar image.Rectangle, visited map[image.Point]struct{}) *visitable {
- if visited == nil {
- visited = map[image.Point]struct{}{}
- }
- return &visitable{
- area: ar,
- visited: visited,
- }
-}
-
-// neighborsAt returns all valid neighbors for the specified point.
-func (v *visitable) neighborsAt(p image.Point) []image.Point {
- var res []image.Point
- for _, neigh := range []image.Point{
- {p.X - 1, p.Y}, // left
- {p.X + 1, p.Y}, // right
- {p.X, p.Y - 1}, // up
- {p.X, p.Y + 1}, // down
- } {
- if !neigh.In(v.area) {
- continue
- }
- if _, ok := v.visited[neigh]; ok {
- continue
- }
- v.visited[neigh] = struct{}{}
- res = append(res, neigh)
- }
- return res
-}
-
-// brailleDFS visits every point in the area and runs the visitor function.
-func brailleDFS(v *visitable, p image.Point, visitFn func(image.Point) error) error {
- neigh := v.neighborsAt(p)
- if len(neigh) == 0 {
- return nil
- }
-
- for _, n := range neigh {
- if err := visitFn(n); err != nil {
- return err
- }
- if err := brailleDFS(v, n, visitFn); err != nil {
- return err
- }
- }
- return nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_line.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_line.go
deleted file mode 100644
index c9f412321..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/braille_line.go
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// braille_line.go contains code that draws lines on a braille canvas.
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/canvas/braille"
- "github.com/mum4k/termdash/private/numbers"
-)
-
-// braillePixelChange represents an action on a pixel on the braille canvas.
-type braillePixelChange int
-
-// String implements fmt.Stringer()
-func (bpc braillePixelChange) String() string {
- if n, ok := braillePixelChangeNames[bpc]; ok {
- return n
- }
- return "braillePixelChangeUnknown"
-}
-
-// braillePixelChangeNames maps braillePixelChange values to human readable names.
-var braillePixelChangeNames = map[braillePixelChange]string{
- braillePixelChangeSet: "braillePixelChangeSet",
- braillePixelChangeClear: "braillePixelChangeClear",
-}
-
-const (
- braillePixelChangeUnknown braillePixelChange = iota
-
- braillePixelChangeSet
- braillePixelChangeClear
-)
-
-// BrailleLineOption is used to provide options to BrailleLine().
-type BrailleLineOption interface {
- // set sets the provided option.
- set(*brailleLineOptions)
-}
-
-// brailleLineOptions stores the provided options.
-type brailleLineOptions struct {
- cellOpts []cell.Option
- pixelChange braillePixelChange
-}
-
-// newBrailleLineOptions returns a new brailleLineOptions instance.
-func newBrailleLineOptions() *brailleLineOptions {
- return &brailleLineOptions{
- pixelChange: braillePixelChangeSet,
- }
-}
-
-// brailleLineOption implements BrailleLineOption.
-type brailleLineOption func(*brailleLineOptions)
-
-// set implements BrailleLineOption.set.
-func (o brailleLineOption) set(opts *brailleLineOptions) {
- o(opts)
-}
-
-// BrailleLineCellOpts sets options on the cells that contain the line.
-// Cell options on a braille canvas can only be set on the entire cell, not per
-// pixel.
-func BrailleLineCellOpts(cOpts ...cell.Option) BrailleLineOption {
- return brailleLineOption(func(opts *brailleLineOptions) {
- opts.cellOpts = cOpts
- })
-}
-
-// BrailleLineClearPixels changes the behavior of BrailleLine, so that it
-// clears the pixels belonging to the line instead of setting them.
-// Useful in order to "erase" a line from the canvas as opposed to drawing one.
-func BrailleLineClearPixels() BrailleLineOption {
- return brailleLineOption(func(opts *brailleLineOptions) {
- opts.pixelChange = braillePixelChangeClear
- })
-}
-
-// BrailleLine draws an approximated line segment on the braille canvas between
-// the two provided points.
-// Both start and end must be valid points within the canvas. Start and end can
-// be the same point in which case only one pixel will be set on the braille
-// canvas.
-// The start or end coordinates must not be negative.
-func BrailleLine(bc *braille.Canvas, start, end image.Point, opts ...BrailleLineOption) error {
- if start.X < 0 || start.Y < 0 {
- return fmt.Errorf("the start coordinates cannot be negative, got: %v", start)
- }
- if end.X < 0 || end.Y < 0 {
- return fmt.Errorf("the end coordinates cannot be negative, got: %v", end)
- }
-
- opt := newBrailleLineOptions()
- for _, o := range opts {
- o.set(opt)
- }
-
- points := brailleLinePoints(start, end)
- for _, p := range points {
- switch opt.pixelChange {
- case braillePixelChangeSet:
- if err := bc.SetPixel(p, opt.cellOpts...); err != nil {
- return fmt.Errorf("bc.SetPixel(%v) => %v", p, err)
- }
- case braillePixelChangeClear:
- if err := bc.ClearPixel(p, opt.cellOpts...); err != nil {
- return fmt.Errorf("bc.ClearPixel(%v) => %v", p, err)
- }
- }
- }
- return nil
-}
-
-// brailleLinePoints returns the points to set when drawing the line.
-func brailleLinePoints(start, end image.Point) []image.Point {
- // Implements Bresenham's line algorithm.
- // https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
-
- vertProj := numbers.Abs(end.Y - start.Y)
- horizProj := numbers.Abs(end.X - start.X)
- if vertProj < horizProj {
- if start.X > end.X {
- return lineLow(end.X, end.Y, start.X, start.Y)
- }
- return lineLow(start.X, start.Y, end.X, end.Y)
- }
- if start.Y > end.Y {
- return lineHigh(end.X, end.Y, start.X, start.Y)
- }
- return lineHigh(start.X, start.Y, end.X, end.Y)
-}
-
-// lineLow returns points that create a line whose horizontal projection
-// (end.X - start.X) is longer than its vertical projection
-// (end.Y - start.Y).
-func lineLow(x0, y0, x1, y1 int) []image.Point {
- deltaX := x1 - x0
- deltaY := y1 - y0
-
- stepY := 1
- if deltaY < 0 {
- stepY = -1
- deltaY = -deltaY
- }
-
- var res []image.Point
- diff := 2*deltaY - deltaX
- y := y0
- for x := x0; x <= x1; x++ {
- res = append(res, image.Point{x, y})
- if diff > 0 {
- y += stepY
- diff -= 2 * deltaX
- }
- diff += 2 * deltaY
- }
- return res
-}
-
-// lineHigh returns points that createa line whose vertical projection
-// (end.Y - start.Y) is longer than its horizontal projection
-// (end.X - start.X).
-func lineHigh(x0, y0, x1, y1 int) []image.Point {
- deltaX := x1 - x0
- deltaY := y1 - y0
-
- stepX := 1
- if deltaX < 0 {
- stepX = -1
- deltaX = -deltaX
- }
-
- var res []image.Point
- diff := 2*deltaX - deltaY
- x := x0
- for y := y0; y <= y1; y++ {
- res = append(res, image.Point{x, y})
-
- if diff > 0 {
- x += stepX
- diff -= 2 * deltaY
- }
- diff += 2 * deltaX
- }
- return res
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/draw.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/draw.go
deleted file mode 100644
index 37c01bf7e..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/draw.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package draw provides functions that draw lines, shapes, etc on 2-D terminal
-// like canvases.
-package draw
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line.go
deleted file mode 100644
index 35318f42d..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line.go
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// hv_line.go contains code that draws horizontal and vertical lines.
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/linestyle"
- "github.com/mum4k/termdash/private/canvas"
-)
-
-// HVLineOption is used to provide options to HVLine().
-type HVLineOption interface {
- // set sets the provided option.
- set(*hVLineOptions)
-}
-
-// hVLineOptions stores the provided options.
-type hVLineOptions struct {
- cellOpts []cell.Option
- lineStyle linestyle.LineStyle
-}
-
-// newHVLineOptions returns a new hVLineOptions instance.
-func newHVLineOptions() *hVLineOptions {
- return &hVLineOptions{
- lineStyle: DefaultLineStyle,
- }
-}
-
-// hVLineOption implements HVLineOption.
-type hVLineOption func(*hVLineOptions)
-
-// set implements HVLineOption.set.
-func (o hVLineOption) set(opts *hVLineOptions) {
- o(opts)
-}
-
-// DefaultLineStyle is the default value for the HVLineStyle option.
-const DefaultLineStyle = linestyle.Light
-
-// HVLineStyle sets the style of the line.
-// Defaults to DefaultLineStyle.
-func HVLineStyle(ls linestyle.LineStyle) HVLineOption {
- return hVLineOption(func(opts *hVLineOptions) {
- opts.lineStyle = ls
- })
-}
-
-// HVLineCellOpts sets options on the cells that contain the line.
-func HVLineCellOpts(cOpts ...cell.Option) HVLineOption {
- return hVLineOption(func(opts *hVLineOptions) {
- opts.cellOpts = cOpts
- })
-}
-
-// HVLine represents one horizontal or vertical line.
-type HVLine struct {
- // Start is the cell where the line starts.
- Start image.Point
- // End is the cell where the line ends.
- End image.Point
-}
-
-// HVLines draws horizontal or vertical lines. Handles drawing of the correct
-// characters for locations where any two lines cross (e.g. a corner, a T shape
-// or a cross). Each line must be at least two cells long. Both start and end
-// must be on the same horizontal (same X coordinate) or same vertical (same Y
-// coordinate) line.
-func HVLines(c *canvas.Canvas, lines []HVLine, opts ...HVLineOption) error {
- opt := newHVLineOptions()
- for _, o := range opts {
- o.set(opt)
- }
-
- g := newHVLineGraph()
- for _, l := range lines {
- line, err := newHVLine(c, l.Start, l.End, opt)
- if err != nil {
- return err
- }
- g.addLine(line)
-
- switch {
- case line.horizontal():
- for curX := line.start.X; ; curX++ {
- cur := image.Point{curX, line.start.Y}
- if _, err := c.SetCell(cur, line.mainPart, opt.cellOpts...); err != nil {
- return err
- }
-
- if curX == line.end.X {
- break
- }
- }
-
- case line.vertical():
- for curY := line.start.Y; ; curY++ {
- cur := image.Point{line.start.X, curY}
- if _, err := c.SetCell(cur, line.mainPart, opt.cellOpts...); err != nil {
- return err
- }
-
- if curY == line.end.Y {
- break
- }
- }
- }
- }
-
- for _, n := range g.multiEdgeNodes() {
- r, err := n.rune(opt.lineStyle)
- if err != nil {
- return err
- }
- if _, err := c.SetCell(n.p, r, opt.cellOpts...); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// hVLine represents a line that will be drawn on the canvas.
-type hVLine struct {
- // start is the starting point of the line.
- start image.Point
-
- // end is the ending point of the line.
- end image.Point
-
- // mainPart is either parts[vLine] or parts[hLine] depending on whether
- // this is horizontal or vertical line.
- mainPart rune
-
- // opts are the options provided in a call to HVLine().
- opts *hVLineOptions
-}
-
-// newHVLine creates a new hVLine instance.
-// Swaps start and end if necessary, so that horizontal drawing is always left
-// to right and vertical is always top down.
-func newHVLine(c *canvas.Canvas, start, end image.Point, opts *hVLineOptions) (*hVLine, error) {
- if ar := c.Area(); !start.In(ar) || !end.In(ar) {
- return nil, fmt.Errorf("both the start%v and the end%v must be in the canvas area: %v", start, end, ar)
- }
-
- parts, err := lineParts(opts.lineStyle)
- if err != nil {
- return nil, err
- }
-
- var mainPart rune
- switch {
- case start.X != end.X && start.Y != end.Y:
- return nil, fmt.Errorf("can only draw horizontal (same X coordinates) or vertical (same Y coordinates), got start:%v end:%v", start, end)
-
- case start.X == end.X && start.Y == end.Y:
- return nil, fmt.Errorf("the line must at least one cell long, got start%v, end%v", start, end)
-
- case start.X == end.X:
- mainPart = parts[vLine]
- if start.Y > end.Y {
- start, end = end, start
- }
-
- case start.Y == end.Y:
- mainPart = parts[hLine]
- if start.X > end.X {
- start, end = end, start
- }
-
- }
-
- return &hVLine{
- start: start,
- end: end,
- mainPart: mainPart,
- opts: opts,
- }, nil
-}
-
-// horizontal determines if this is a horizontal line.
-func (hvl *hVLine) horizontal() bool {
- return hvl.mainPart == lineStyleChars[hvl.opts.lineStyle][hLine]
-}
-
-// vertical determines if this is a vertical line.
-func (hvl *hVLine) vertical() bool {
- return hvl.mainPart == lineStyleChars[hvl.opts.lineStyle][vLine]
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line_graph.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line_graph.go
deleted file mode 100644
index ccbc72a57..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/hv_line_graph.go
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// hv_line_graph.go helps to keep track of locations where lines cross.
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/linestyle"
-)
-
-// hVLineEdge is an edge between two points on the graph.
-type hVLineEdge struct {
- // from is the starting node of this edge.
- // From is guaranteed to be less than to.
- from image.Point
-
- // to is the ending point of this edge.
- to image.Point
-}
-
-// newHVLineEdge returns a new edge between the two points.
-func newHVLineEdge(from, to image.Point) hVLineEdge {
- return hVLineEdge{
- from: from,
- to: to,
- }
-}
-
-// hVLineNode represents one node in the graph.
-// I.e. one cell.
-type hVLineNode struct {
- // p is the point where this node is.
- p image.Point
-
- // edges are the edges between this node and the surrounding nodes.
- // The code only supports horizontal and vertical lines so there can only
- // ever be edges to nodes on these planes.
- edges map[hVLineEdge]bool
-}
-
-// newHVLineNode creates a new newHVLineNode.
-func newHVLineNode(p image.Point) *hVLineNode {
- return &hVLineNode{
- p: p,
- edges: map[hVLineEdge]bool{},
- }
-}
-
-// hasDown determines if this node has an edge to the one below it.
-func (n *hVLineNode) hasDown() bool {
- target := newHVLineEdge(n.p, image.Point{n.p.X, n.p.Y + 1})
- _, ok := n.edges[target]
- return ok
-}
-
-// hasUp determines if this node has an edge to the one above it.
-func (n *hVLineNode) hasUp() bool {
- target := newHVLineEdge(image.Point{n.p.X, n.p.Y - 1}, n.p)
- _, ok := n.edges[target]
- return ok
-}
-
-// hasLeft determines if this node has an edge to the next node on the left.
-func (n *hVLineNode) hasLeft() bool {
- target := newHVLineEdge(image.Point{n.p.X - 1, n.p.Y}, n.p)
- _, ok := n.edges[target]
- return ok
-}
-
-// hasRight determines if this node has an edge to the next node on the right.
-func (n *hVLineNode) hasRight() bool {
- target := newHVLineEdge(n.p, image.Point{n.p.X + 1, n.p.Y})
- _, ok := n.edges[target]
- return ok
-}
-
-// rune, given the selected line style returns the correct line character to
-// represent this node.
-// Only handles nodes with two or more edges, as returned by multiEdgeNodes().
-func (n *hVLineNode) rune(ls linestyle.LineStyle) (rune, error) {
- parts, err := lineParts(ls)
- if err != nil {
- return -1, err
- }
-
- switch len(n.edges) {
- case 2:
- switch {
- case n.hasLeft() && n.hasRight():
- return parts[hLine], nil
- case n.hasUp() && n.hasDown():
- return parts[vLine], nil
- case n.hasDown() && n.hasRight():
- return parts[topLeftCorner], nil
- case n.hasDown() && n.hasLeft():
- return parts[topRightCorner], nil
- case n.hasUp() && n.hasRight():
- return parts[bottomLeftCorner], nil
- case n.hasUp() && n.hasLeft():
- return parts[bottomRightCorner], nil
- default:
- return -1, fmt.Errorf("unexpected two edges in node representing point %v: %v", n.p, n.edges)
- }
-
- case 3:
- switch {
- case n.hasUp() && n.hasLeft() && n.hasRight():
- return parts[hAndUp], nil
- case n.hasDown() && n.hasLeft() && n.hasRight():
- return parts[hAndDown], nil
- case n.hasUp() && n.hasDown() && n.hasRight():
- return parts[vAndRight], nil
- case n.hasUp() && n.hasDown() && n.hasLeft():
- return parts[vAndLeft], nil
-
- default:
- return -1, fmt.Errorf("unexpected three edges in node representing point %v: %v", n.p, n.edges)
- }
-
- case 4:
- return parts[vAndH], nil
- default:
- return -1, fmt.Errorf("unexpected number of edges(%d) in node representing point %v", len(n.edges), n.p)
- }
-}
-
-// hVLineGraph represents lines on the canvas as a bidirectional graph of
-// nodes. Helps to determine the characters that should be used where multiple
-// lines cross.
-type hVLineGraph struct {
- nodes map[image.Point]*hVLineNode
-}
-
-// newHVLineGraph creates a new hVLineGraph.
-func newHVLineGraph() *hVLineGraph {
- return &hVLineGraph{
- nodes: make(map[image.Point]*hVLineNode),
- }
-}
-
-// getOrCreateNode gets an existing or creates a new node for the point.
-func (g *hVLineGraph) getOrCreateNode(p image.Point) *hVLineNode {
- if n, ok := g.nodes[p]; ok {
- return n
- }
- n := newHVLineNode(p)
- g.nodes[p] = n
- return n
-}
-
-// addLine adds a line to the graph.
-// This adds edges between all the points on the line.
-func (g *hVLineGraph) addLine(line *hVLine) {
- switch {
- case line.horizontal():
- for curX := line.start.X; curX < line.end.X; curX++ {
- from := image.Point{curX, line.start.Y}
- to := image.Point{curX + 1, line.start.Y}
- n1 := g.getOrCreateNode(from)
- n2 := g.getOrCreateNode(to)
- edge := newHVLineEdge(from, to)
- n1.edges[edge] = true
- n2.edges[edge] = true
- }
-
- case line.vertical():
- for curY := line.start.Y; curY < line.end.Y; curY++ {
- from := image.Point{line.start.X, curY}
- to := image.Point{line.start.X, curY + 1}
- n1 := g.getOrCreateNode(from)
- n2 := g.getOrCreateNode(to)
- edge := newHVLineEdge(from, to)
- n1.edges[edge] = true
- n2.edges[edge] = true
- }
- }
-}
-
-// multiEdgeNodes returns all nodes that have more than one edge. These are
-// the nodes where we might need to use different line characters to represent
-// the crossing of multiple lines.
-func (g *hVLineGraph) multiEdgeNodes() []*hVLineNode {
- var nodes []*hVLineNode
- for _, n := range g.nodes {
- if len(n.edges) <= 1 {
- continue
- }
- nodes = append(nodes, n)
- }
- return nodes
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/line_style.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/line_style.go
deleted file mode 100644
index 41f1df4ee..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/line_style.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-import (
- "fmt"
-
- "github.com/mum4k/termdash/linestyle"
- "github.com/mum4k/termdash/private/runewidth"
-)
-
-// line_style.go contains the Unicode characters used for drawing lines of
-// different styles.
-
-// lineStyleChars maps the line styles to the corresponding component characters.
-// Source: http://en.wikipedia.org/wiki/Box-drawing_character.
-var lineStyleChars = map[linestyle.LineStyle]map[linePart]rune{
- linestyle.Light: {
- hLine: '─',
- vLine: '│',
- topLeftCorner: '┌',
- topRightCorner: '┐',
- bottomLeftCorner: '└',
- bottomRightCorner: '┘',
- hAndUp: '┴',
- hAndDown: '┬',
- vAndLeft: '┤',
- vAndRight: '├',
- vAndH: '┼',
- },
- linestyle.Double: {
- hLine: '═',
- vLine: '║',
- topLeftCorner: '╔',
- topRightCorner: '╗',
- bottomLeftCorner: '╚',
- bottomRightCorner: '╝',
- hAndUp: '╩',
- hAndDown: '╦',
- vAndLeft: '╣',
- vAndRight: '╠',
- vAndH: '╬',
- },
- linestyle.Round: {
- hLine: '─',
- vLine: '│',
- topLeftCorner: '╭',
- topRightCorner: '╮',
- bottomLeftCorner: '╰',
- bottomRightCorner: '╯',
- hAndUp: '┴',
- hAndDown: '┬',
- vAndLeft: '┤',
- vAndRight: '├',
- vAndH: '┼',
- },
-}
-
-// init verifies that all line parts are half-width runes (occupy only one
-// cell).
-func init() {
- for ls, parts := range lineStyleChars {
- for part, r := range parts {
- if got := runewidth.RuneWidth(r); got > 1 {
- panic(fmt.Errorf("line style %v line part %v is a rune %c with width %v, all parts must be half-width runes (width of one)", ls, part, r, got))
- }
- }
- }
-}
-
-// lineParts returns the line component characters for the provided line style.
-func lineParts(ls linestyle.LineStyle) (map[linePart]rune, error) {
- parts, ok := lineStyleChars[ls]
- if !ok {
- return nil, fmt.Errorf("unsupported line style %d", ls)
- }
- return parts, nil
-}
-
-// linePart identifies individual line parts.
-type linePart int
-
-// String implements fmt.Stringer()
-func (lp linePart) String() string {
- if n, ok := linePartNames[lp]; ok {
- return n
- }
- return "linePartUnknown"
-}
-
-// linePartNames maps linePart values to human readable names.
-var linePartNames = map[linePart]string{
- vLine: "linePartVLine",
- topLeftCorner: "linePartTopLeftCorner",
- topRightCorner: "linePartTopRightCorner",
- bottomLeftCorner: "linePartBottomLeftCorner",
- bottomRightCorner: "linePartBottomRightCorner",
- hAndUp: "linePartHAndUp",
- hAndDown: "linePartHAndDown",
- vAndLeft: "linePartVAndLeft",
- vAndRight: "linePartVAndRight",
- vAndH: "linePartVAndH",
-}
-
-const (
- hLine linePart = iota
- vLine
- topLeftCorner
- topRightCorner
- bottomLeftCorner
- bottomRightCorner
- hAndUp
- hAndDown
- vAndLeft
- vAndRight
- vAndH
-)
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/rectangle.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/rectangle.go
deleted file mode 100644
index cd96ff715..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/rectangle.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// rectangle.go draws a rectangle.
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/canvas"
-)
-
-// RectangleOption is used to provide options to the Rectangle function.
-type RectangleOption interface {
- // set sets the provided option.
- set(*rectOptions)
-}
-
-// rectOptions stores the provided options.
-type rectOptions struct {
- cellOpts []cell.Option
- char rune
-}
-
-// rectOption implements RectangleOption.
-type rectOption func(rOpts *rectOptions)
-
-// set implements RectangleOption.set.
-func (ro rectOption) set(rOpts *rectOptions) {
- ro(rOpts)
-}
-
-// RectCellOpts sets options on the cells that create the rectangle.
-func RectCellOpts(opts ...cell.Option) RectangleOption {
- return rectOption(func(rOpts *rectOptions) {
- rOpts.cellOpts = append(rOpts.cellOpts, opts...)
- })
-}
-
-// DefaultRectChar is the default value for the RectChar option.
-const DefaultRectChar = ' '
-
-// RectChar sets the character used in each of the cells of the rectangle.
-func RectChar(c rune) RectangleOption {
- return rectOption(func(rOpts *rectOptions) {
- rOpts.char = c
- })
-}
-
-// Rectangle draws a filled rectangle on the canvas.
-func Rectangle(c *canvas.Canvas, r image.Rectangle, opts ...RectangleOption) error {
- opt := &rectOptions{
- char: DefaultRectChar,
- }
- for _, o := range opts {
- o.set(opt)
- }
-
- if ar := c.Area(); !r.In(ar) {
- return fmt.Errorf("the requested rectangle %v doesn't fit the canvas area %v", r, ar)
- }
-
- if r.Dx() < 1 || r.Dy() < 1 {
- return fmt.Errorf("the rectangle must be at least 1x1 cell, got %v", r)
- }
-
- for col := r.Min.X; col < r.Max.X; col++ {
- for row := r.Min.Y; row < r.Max.Y; row++ {
- cells, err := c.SetCell(image.Point{col, row}, opt.char, opt.cellOpts...)
- if err != nil {
- return err
- }
- if cells != 1 {
- return fmt.Errorf("invalid rectangle character %q, this character occupies %d cells, the implementation only supports half-width runes that occupy exactly one cell", opt.char, cells)
- }
- }
- }
- return nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/text.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/text.go
deleted file mode 100644
index 17c4954a0..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/text.go
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// text.go contains code that prints UTF-8 encoded strings on the canvas.
-
-import (
- "fmt"
- "image"
- "strings"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/canvas"
- "github.com/mum4k/termdash/private/runewidth"
-)
-
-// OverrunMode represents
-type OverrunMode int
-
-// String implements fmt.Stringer()
-func (om OverrunMode) String() string {
- if n, ok := overrunModeNames[om]; ok {
- return n
- }
- return "OverrunModeUnknown"
-}
-
-// overrunModeNames maps OverrunMode values to human readable names.
-var overrunModeNames = map[OverrunMode]string{
- OverrunModeStrict: "OverrunModeStrict",
- OverrunModeTrim: "OverrunModeTrim",
- OverrunModeThreeDot: "OverrunModeThreeDot",
-}
-
-const (
- // OverrunModeStrict verifies that the drawn value fits the canvas and
- // returns an error if it doesn't.
- OverrunModeStrict OverrunMode = iota
-
- // OverrunModeTrim trims the part of the text that doesn't fit.
- OverrunModeTrim
-
- // OverrunModeThreeDot trims the text and places the horizontal ellipsis
- // '…' character at the end.
- OverrunModeThreeDot
-)
-
-// TextOption is used to provide options to Text().
-type TextOption interface {
- // set sets the provided option.
- set(*textOptions)
-}
-
-// textOptions stores the provided options.
-type textOptions struct {
- cellOpts []cell.Option
- maxX int
- overrunMode OverrunMode
-}
-
-// textOption implements TextOption.
-type textOption func(*textOptions)
-
-// set implements TextOption.set.
-func (to textOption) set(tOpts *textOptions) {
- to(tOpts)
-}
-
-// TextCellOpts sets options on the cells that contain the text.
-func TextCellOpts(opts ...cell.Option) TextOption {
- return textOption(func(tOpts *textOptions) {
- tOpts.cellOpts = opts
- })
-}
-
-// TextMaxX sets a limit on the X coordinate (column) of the drawn text.
-// The X coordinate of all cells used by the text must be within
-// start.X <= X < TextMaxX.
-// If not provided, the width of the canvas is used as TextMaxX.
-func TextMaxX(x int) TextOption {
- return textOption(func(tOpts *textOptions) {
- tOpts.maxX = x
- })
-}
-
-// TextOverrunMode indicates what to do with text that overruns the TextMaxX()
-// or the width of the canvas if TextMaxX() isn't specified.
-// Defaults to OverrunModeStrict.
-func TextOverrunMode(om OverrunMode) TextOption {
- return textOption(func(tOpts *textOptions) {
- tOpts.overrunMode = om
- })
-}
-
-// TrimText trims the provided text so that it fits the specified amount of cells.
-func TrimText(text string, maxCells int, om OverrunMode) (string, error) {
- if maxCells < 1 {
- return "", fmt.Errorf("maxCells(%d) cannot be less than one", maxCells)
- }
-
- textCells := runewidth.StringWidth(text)
- if textCells <= maxCells {
- // Nothing to do if the text fits.
- return text, nil
- }
-
- switch om {
- case OverrunModeStrict:
- return "", fmt.Errorf("the requested text %q takes %d cells to draw, space is available for only %d cells and overrun mode is %v", text, textCells, maxCells, om)
- case OverrunModeTrim, OverrunModeThreeDot:
- default:
- return "", fmt.Errorf("unsupported overrun mode %d", om)
- }
-
- var b strings.Builder
- cur := 0
- for _, r := range text {
- rw := runewidth.RuneWidth(r)
- if cur+rw >= maxCells {
- switch {
- case om == OverrunModeTrim:
- // Only write the rune if it still fits, i.e. don't cut
- // full-width runes in half.
- if cur+rw == maxCells {
- b.WriteRune(r)
- }
- case om == OverrunModeThreeDot:
- b.WriteRune('…')
- }
- break
- }
-
- b.WriteRune(r)
- cur += rw
- }
- return b.String(), nil
-}
-
-// Text prints the provided text on the canvas starting at the provided point.
-func Text(c *canvas.Canvas, text string, start image.Point, opts ...TextOption) error {
- ar := c.Area()
- if !start.In(ar) {
- return fmt.Errorf("the requested start point %v falls outside of the provided canvas %v", start, ar)
- }
-
- opt := &textOptions{}
- for _, o := range opts {
- o.set(opt)
- }
-
- if opt.maxX < 0 || opt.maxX > ar.Max.X {
- return fmt.Errorf("invalid TextMaxX(%v), must be a positive number that is <= canvas.width %v", opt.maxX, ar.Dx())
- }
-
- var wantMaxX int
- if opt.maxX == 0 {
- wantMaxX = ar.Max.X
- } else {
- wantMaxX = opt.maxX
- }
-
- maxCells := wantMaxX - start.X
- trimmed, err := TrimText(text, maxCells, opt.overrunMode)
- if err != nil {
- return err
- }
-
- cur := start
- for _, r := range trimmed {
- cells, err := c.SetCell(cur, r, opt.cellOpts...)
- if err != nil {
- return err
- }
- cur = image.Point{cur.X + cells, cur.Y}
- }
- return nil
-}
-
-// ResizeNeeded draws an unicode character indicating that the canvas size is
-// too small to draw meaningful content.
-func ResizeNeeded(cvs *canvas.Canvas) error {
- return Text(cvs, "⇄", image.Point{0, 0})
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/vertical_text.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/vertical_text.go
deleted file mode 100644
index 44aadc9e5..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/draw/vertical_text.go
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package draw
-
-// vertical_text.go contains code that prints UTF-8 encoded strings on the
-// canvas in vertical columns instead of lines.
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/canvas"
-)
-
-// VerticalTextOption is used to provide options to Text().
-type VerticalTextOption interface {
- // set sets the provided option.
- set(*verticalTextOptions)
-}
-
-// verticalTextOptions stores the provided options.
-type verticalTextOptions struct {
- cellOpts []cell.Option
- maxY int
- overrunMode OverrunMode
-}
-
-// verticalTextOption implements VerticalTextOption.
-type verticalTextOption func(*verticalTextOptions)
-
-// set implements VerticalTextOption.set.
-func (vto verticalTextOption) set(vtOpts *verticalTextOptions) {
- vto(vtOpts)
-}
-
-// VerticalTextCellOpts sets options on the cells that contain the text.
-func VerticalTextCellOpts(opts ...cell.Option) VerticalTextOption {
- return verticalTextOption(func(vtOpts *verticalTextOptions) {
- vtOpts.cellOpts = opts
- })
-}
-
-// VerticalTextMaxY sets a limit on the Y coordinate (row) of the drawn text.
-// The Y coordinate of all cells used by the vertical text must be within
-// start.Y <= Y < VerticalTextMaxY.
-// If not provided, the height of the canvas is used as VerticalTextMaxY.
-func VerticalTextMaxY(y int) VerticalTextOption {
- return verticalTextOption(func(vtOpts *verticalTextOptions) {
- vtOpts.maxY = y
- })
-}
-
-// VerticalTextOverrunMode indicates what to do with text that overruns the
-// VerticalTextMaxY() or the width of the canvas if VerticalTextMaxY() isn't
-// specified.
-// Defaults to OverrunModeStrict.
-func VerticalTextOverrunMode(om OverrunMode) VerticalTextOption {
- return verticalTextOption(func(vtOpts *verticalTextOptions) {
- vtOpts.overrunMode = om
- })
-}
-
-// VerticalText prints the provided text on the canvas starting at the provided point.
-// The text is printed in a vertical orientation, i.e:
-// H
-// e
-// l
-// l
-// o
-func VerticalText(c *canvas.Canvas, text string, start image.Point, opts ...VerticalTextOption) error {
- ar := c.Area()
- if !start.In(ar) {
- return fmt.Errorf("the requested start point %v falls outside of the provided canvas %v", start, ar)
- }
-
- opt := &verticalTextOptions{}
- for _, o := range opts {
- o.set(opt)
- }
-
- if opt.maxY < 0 || opt.maxY > ar.Max.Y {
- return fmt.Errorf("invalid VerticalTextMaxY(%v), must be a positive number that is <= canvas.width %v", opt.maxY, ar.Dy())
- }
-
- var wantMaxY int
- if opt.maxY == 0 {
- wantMaxY = ar.Max.Y
- } else {
- wantMaxY = opt.maxY
- }
-
- maxCells := wantMaxY - start.Y
- trimmed, err := TrimText(text, maxCells, opt.overrunMode)
- if err != nil {
- return err
- }
-
- cur := start
- for _, r := range trimmed {
- cells, err := c.SetCell(cur, r, opt.cellOpts...)
- if err != nil {
- return err
- }
- cur = image.Point{cur.X, cur.Y + cells}
- }
- return nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/event.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/event.go
deleted file mode 100644
index e9ef18dbb..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/event.go
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package event provides a non-blocking event distribution and subscription
-// system.
-package event
-
-import (
- "context"
- "reflect"
- "sync"
-
- "github.com/mum4k/termdash/private/event/eventqueue"
- "github.com/mum4k/termdash/terminal/terminalapi"
-)
-
-// Callback is a function provided by an event subscriber.
-// It gets called with each event that passed the subscription filter.
-// Implementations must be thread-safe, events come from a separate goroutine.
-// Implementation should be light-weight, otherwise a slow-processing
-// subscriber can build a long tail of events.
-type Callback func(terminalapi.Event)
-
-// queue is a queue of terminal events.
-type queue interface {
- Push(e terminalapi.Event)
- Pull(ctx context.Context) terminalapi.Event
- Close()
-}
-
-// subscriber represents a single subscriber.
-type subscriber struct {
- // cb is the callback the subscriber receives events on.
- cb Callback
-
- // filter filters events towards the subscriber.
- // An empty filter receives all events.
- filter map[reflect.Type]bool
-
- // queue is a queue of events towards the subscriber.
- queue queue
-
- // cancel when called terminates the goroutine that forwards events towards
- // this subscriber.
- cancel context.CancelFunc
-
- // processes is the number of events that were fully processed, i.e.
- // delivered to the callback.
- processed int
-
- // mu protects busy.
- mu sync.Mutex
-}
-
-// newSubscriber creates a new event subscriber.
-func newSubscriber(filter []terminalapi.Event, cb Callback, opts *subscribeOptions) *subscriber {
- f := map[reflect.Type]bool{}
- for _, ev := range filter {
- f[reflect.TypeOf(ev)] = true
- }
-
- ctx, cancel := context.WithCancel(context.Background())
- var q queue
- if opts.throttle {
- q = eventqueue.NewThrottled(opts.maxRep)
- } else {
- q = eventqueue.New()
- }
-
- s := &subscriber{
- cb: cb,
- filter: f,
- queue: q,
- cancel: cancel,
- }
-
- // Terminates when stop() is called.
- go s.run(ctx)
- return s
-}
-
-// callback sends the event to the callback.
-func (s *subscriber) callback(ev terminalapi.Event) {
- s.cb(ev)
-
- func() {
- s.mu.Lock()
- defer s.mu.Unlock()
- s.processed++
- }()
-}
-
-// run periodically forwards events towards the subscriber.
-// Terminates when the context expires.
-func (s *subscriber) run(ctx context.Context) {
- for {
- ev := s.queue.Pull(ctx)
- if ev != nil {
- s.callback(ev)
- }
-
- select {
- case <-ctx.Done():
- return
- default:
- }
- }
-}
-
-// event forwards an event to the subscriber.
-func (s *subscriber) event(ev terminalapi.Event) {
- if len(s.filter) == 0 {
- s.queue.Push(ev)
- }
-
- t := reflect.TypeOf(ev)
- if s.filter[t] {
- s.queue.Push(ev)
- }
-}
-
-// processedEvents returns the number of events processed by this subscriber.
-func (s *subscriber) processedEvents() int {
- s.mu.Lock()
- defer s.mu.Unlock()
- return s.processed
-}
-
-// stop stops the event subscriber.
-func (s *subscriber) stop() {
- s.cancel()
- s.queue.Close()
-}
-
-// DistributionSystem distributes events to subscribers.
-//
-// Subscribers can request filtering of events they get based on event type or
-// subscribe to all events.
-//
-// The distribution system maintains a queue towards each subscriber, making
-// sure that a single slow subscriber only slows itself down, rather than the
-// entire application.
-//
-// This object is thread-safe.
-type DistributionSystem struct {
- // subscribers subscribe to events.
- // maps subscriber id to subscriber.
- subscribers map[int]*subscriber
-
- // nextID is id for the next subscriber.
- nextID int
-
- // mu protects the distribution system.
- mu sync.Mutex
-}
-
-// NewDistributionSystem creates a new event distribution system.
-func NewDistributionSystem() *DistributionSystem {
- return &DistributionSystem{
- subscribers: map[int]*subscriber{},
- }
-}
-
-// Event should be called with events coming from the terminal.
-// The distribution system will distribute these to all the subscribers.
-func (eds *DistributionSystem) Event(ev terminalapi.Event) {
- eds.mu.Lock()
- defer eds.mu.Unlock()
-
- for _, sub := range eds.subscribers {
- sub.event(ev)
- }
-}
-
-// StopFunc when called unsubscribes the subscriber from all events and
-// releases resources tied to the subscriber.
-type StopFunc func()
-
-// SubscribeOption is used to provide options to Subscribe.
-type SubscribeOption interface {
- // set sets the provided option.
- set(*subscribeOptions)
-}
-
-// subscribeOptions stores the provided options.
-type subscribeOptions struct {
- throttle bool
- maxRep int
-}
-
-// subscribeOption implements Option.
-type subscribeOption func(*subscribeOptions)
-
-// set implements SubscribeOption.set.
-func (o subscribeOption) set(sOpts *subscribeOptions) {
- o(sOpts)
-}
-
-// MaxRepetitive when provided, instructs the system to drop repetitive
-// events instead of delivering them.
-// The argument maxRep indicates the maximum number of repetitive events to
-// enqueue towards the subscriber.
-func MaxRepetitive(maxRep int) SubscribeOption {
- return subscribeOption(func(sOpts *subscribeOptions) {
- sOpts.throttle = true
- sOpts.maxRep = maxRep
- })
-}
-
-// Subscribe subscribes to events according to the filter.
-// An empty filter indicates that the subscriber wishes to receive events of
-// all kinds. If the filter is non-empty, only events of the provided type will
-// be sent to the subscriber.
-// Returns a function that allows the subscriber to unsubscribe.
-func (eds *DistributionSystem) Subscribe(filter []terminalapi.Event, cb Callback, opts ...SubscribeOption) StopFunc {
- eds.mu.Lock()
- defer eds.mu.Unlock()
-
- opt := &subscribeOptions{}
- for _, o := range opts {
- o.set(opt)
- }
-
- id := eds.nextID
- eds.nextID++
- sub := newSubscriber(filter, cb, opt)
- eds.subscribers[id] = sub
-
- return func() {
- eds.mu.Lock()
- defer eds.mu.Unlock()
-
- sub.stop()
- delete(eds.subscribers, id)
- }
-}
-
-// Processed returns the number of events that were fully processed, i.e.
-// delivered to all the subscribers and their callbacks returned.
-func (eds *DistributionSystem) Processed() int {
- eds.mu.Lock()
- defer eds.mu.Unlock()
-
- var res int
- for _, sub := range eds.subscribers {
- res += sub.processedEvents()
- }
- return res
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/eventqueue/eventqueue.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/eventqueue/eventqueue.go
deleted file mode 100644
index eb22d4f2c..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/event/eventqueue/eventqueue.go
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package eventqueue provides an unboud FIFO queue of events.
-package eventqueue
-
-import (
- "context"
- "reflect"
- "sync"
- "time"
-
- "github.com/mum4k/termdash/terminal/terminalapi"
-)
-
-// node is a single data item on the queue.
-type node struct {
- prev *node
- next *node
- event terminalapi.Event
-}
-
-// Unbound is an unbound FIFO queue of terminal events.
-// Unbound must not be copied, pass it by reference only.
-// This implementation is thread-safe.
-type Unbound struct {
- first *node
- last *node
- // mu protects first and last.
- mu sync.Mutex
-
- // cond is used to notify any callers waiting on a call to Pull().
- cond *sync.Cond
-
- // condMU protects cond.
- condMU sync.RWMutex
-
- // done is closed when the queue isn't needed anymore.
- done chan struct{}
-}
-
-// New returns a new Unbound queue of terminal events.
-// Call Close() when done with the queue.
-func New() *Unbound {
- u := &Unbound{
- done: make(chan (struct{})),
- }
- u.cond = sync.NewCond(&u.condMU)
- go u.wake() // Stops when Close() is called.
- return u
-}
-
-// wake periodically wakes up all goroutines waiting at Pull() so they can
-// check if the context expired.
-func (u *Unbound) wake() {
- const spinTime = 250 * time.Millisecond
- t := time.NewTicker(spinTime)
- defer t.Stop()
- for {
- select {
- case <-t.C:
- u.cond.Broadcast()
- case <-u.done:
- return
- }
- }
-}
-
-// Empty determines if the queue is empty.
-func (u *Unbound) Empty() bool {
- u.mu.Lock()
- defer u.mu.Unlock()
- return u.empty()
-}
-
-// empty determines if the queue is empty.
-func (u *Unbound) empty() bool {
- return u.first == nil
-}
-
-// Push pushes an event onto the queue.
-func (u *Unbound) Push(e terminalapi.Event) {
- u.mu.Lock()
- defer u.mu.Unlock()
- u.push(e)
-}
-
-// push is the implementation of Push.
-// Caller must hold u.mu.
-func (u *Unbound) push(e terminalapi.Event) {
- n := &node{
- event: e,
- }
- if u.empty() {
- u.first = n
- u.last = n
- } else {
- prev := u.last
- u.last.next = n
- u.last = n
- u.last.prev = prev
- }
- u.cond.Signal()
-}
-
-// Pop pops an event from the queue. Returns nil if the queue is empty.
-func (u *Unbound) Pop() terminalapi.Event {
- u.mu.Lock()
- defer u.mu.Unlock()
-
- if u.empty() {
- return nil
- }
-
- n := u.first
- u.first = u.first.next
-
- if u.empty() {
- u.last = nil
- }
- return n.event
-}
-
-// Pull is like Pop(), but blocks until an item is available or the context
-// expires. Returns a nil event if the context expired.
-func (u *Unbound) Pull(ctx context.Context) terminalapi.Event {
- if e := u.Pop(); e != nil {
- return e
- }
-
- u.cond.L.Lock()
- defer u.cond.L.Unlock()
- for {
- select {
- case <-ctx.Done():
- return nil
- default:
- }
-
- if e := u.Pop(); e != nil {
- return e
- }
- u.cond.Wait()
- }
-}
-
-// Close should be called when the queue isn't needed anymore.
-func (u *Unbound) Close() {
- close(u.done)
-}
-
-// Throttled is an unbound and throttled FIFO queue of terminal events.
-// Throttled must not be copied, pass it by reference only.
-// This implementation is thread-safe.
-type Throttled struct {
- queue *Unbound
- max int
-}
-
-// NewThrottled returns a new Throttled queue of terminal events.
-//
-// This queue scans the queue content on each Push call and won't Push the
-// event if there already is a continuous chain of exactly the same events
-// en queued. The argument maxRep specifies the maximum number of repetitive
-// events.
-//
-// Call Close() when done with the queue.
-func NewThrottled(maxRep int) *Throttled {
- t := &Throttled{
- queue: New(),
- max: maxRep,
- }
- return t
-}
-
-// Empty determines if the queue is empty.
-func (t *Throttled) Empty() bool {
- return t.queue.empty()
-}
-
-// Push pushes an event onto the queue.
-func (t *Throttled) Push(e terminalapi.Event) {
- t.queue.mu.Lock()
- defer t.queue.mu.Unlock()
-
- if t.queue.empty() {
- t.queue.push(e)
- return
- }
-
- var same int
- for n := t.queue.last; n != nil; n = n.prev {
- if reflect.DeepEqual(e, n.event) {
- same++
- } else {
- break
- }
-
- if same > t.max {
- return // Drop the repetitive event.
- }
- }
- t.queue.push(e)
-}
-
-// Pop pops an event from the queue. Returns nil if the queue is empty.
-func (t *Throttled) Pop() terminalapi.Event {
- return t.queue.Pop()
-}
-
-// Pull is like Pop(), but blocks until an item is available or the context
-// expires. Returns a nil event if the context expired.
-func (t *Throttled) Pull(ctx context.Context) terminalapi.Event {
- return t.queue.Pull(ctx)
-}
-
-// Close should be called when the queue isn't needed anymore.
-func (t *Throttled) Close() {
- close(t.queue.done)
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/numbers.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/numbers.go
deleted file mode 100644
index e91620f77..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/numbers.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package numbers implements various numerical functions.
-package numbers
-
-import (
- "image"
- "math"
-)
-
-// RoundToNonZeroPlaces rounds the float up, so that it has at least the provided
-// number of non-zero decimal places.
-// Returns the rounded float and the number of leading decimal places that
-// are zero. Returns the original float when places is zero. Negative places
-// are treated as positive, so that -2 == 2.
-func RoundToNonZeroPlaces(f float64, places int) (float64, int) {
- if f == 0 {
- return 0, 0
- }
-
- decOnly := zeroBeforeDecimal(f)
- if decOnly == 0 {
- return f, 0
- }
- nzMult := multToNonZero(decOnly)
- if places == 0 {
- return f, multToPlaces(nzMult)
- }
- plMult := placesToMult(places)
-
- m := float64(nzMult * plMult)
- return math.Ceil(f*m) / m, multToPlaces(nzMult)
-}
-
-// multToNonZero returns multiplier for the float, so that the first decimal
-// place is non-zero. The float must not be zero.
-func multToNonZero(f float64) int {
- v := f
- if v < 0 {
- v *= -1
- }
-
- mult := 1
- for v < 0.1 {
- v *= 10
- mult *= 10
- }
- return mult
-}
-
-// placesToMult translates the number of decimal places to a multiple of 10.
-func placesToMult(places int) int {
- if places < 0 {
- places *= -1
- }
-
- mult := 1
- for i := 0; i < places; i++ {
- mult *= 10
- }
- return mult
-}
-
-// multToPlaces translates the multiple of 10 to a number of decimal places.
-func multToPlaces(mult int) int {
- places := 0
- for mult > 1 {
- mult /= 10
- places++
- }
- return places
-}
-
-// zeroBeforeDecimal modifies the float so that it only has zero value before
-// the decimal point.
-func zeroBeforeDecimal(f float64) float64 {
- var sign float64 = 1
- if f < 0 {
- f *= -1
- sign = -1
- }
-
- floor := math.Floor(f)
- return (f - floor) * sign
-}
-
-// MinMax returns the smallest and the largest value among the provided values.
-// Returns (0, 0) if there are no values.
-// Ignores NaN values. Allowing NaN values could lead to a corner case where all
-// values can be NaN, in this case the function will return NaN as min and max.
-func MinMax(values []float64) (min, max float64) {
- if len(values) == 0 {
- return 0, 0
- }
- min = math.MaxFloat64
- max = -1 * math.MaxFloat64
- allNaN := true
- for _, v := range values {
- if math.IsNaN(v) {
- continue
- }
- allNaN = false
-
- if v < min {
- min = v
- }
- if v > max {
- max = v
- }
- }
-
- if allNaN {
- return math.NaN(), math.NaN()
- }
-
- return min, max
-}
-
-// MinMaxInts returns the smallest and the largest int value among the provided
-// values. Returns (0, 0) if there are no values.
-func MinMaxInts(values []int) (min, max int) {
- if len(values) == 0 {
- return 0, 0
- }
- min = math.MaxInt32
- max = -1 * math.MaxInt32
-
- for _, v := range values {
- if v < min {
- min = v
- }
- if v > max {
- max = v
- }
- }
- return min, max
-}
-
-// DegreesToRadians converts degrees to the equivalent in radians.
-func DegreesToRadians(degrees int) float64 {
- if degrees > 360 {
- degrees %= 360
- }
- return (float64(degrees) / 180) * math.Pi
-}
-
-// RadiansToDegrees converts radians to the equivalent in degrees.
-func RadiansToDegrees(radians float64) int {
- d := int(math.Round(radians * 180 / math.Pi))
- if d < 0 {
- d += 360
- }
- return d
-}
-
-// Abs returns the absolute value of x.
-func Abs(x int) int {
- if x < 0 {
- return -x
- }
- return x
-}
-
-// findGCF finds the greatest common factor of two integers.
-func findGCF(a, b int) int {
- if a == 0 || b == 0 {
- return 0
- }
- a = Abs(a)
- b = Abs(b)
-
- // https://en.wikipedia.org/wiki/Euclidean_algorithm
- for {
- rem := a % b
- a = b
- b = rem
-
- if b == 0 {
- break
- }
- }
- return a
-}
-
-// SimplifyRatio simplifies the given ratio.
-func SimplifyRatio(ratio image.Point) image.Point {
- gcf := findGCF(ratio.X, ratio.Y)
- if gcf == 0 {
- return image.ZP
- }
- return image.Point{
- X: ratio.X / gcf,
- Y: ratio.Y / gcf,
- }
-}
-
-// SplitByRatio splits the provided number by the specified ratio.
-func SplitByRatio(n int, ratio image.Point) image.Point {
- sr := SimplifyRatio(ratio)
- if sr.Eq(image.ZP) {
- return image.ZP
- }
- fn := float64(n)
- sum := float64(sr.X + sr.Y)
- fact := fn / sum
- return image.Point{
- int(math.Round(fact * float64(sr.X))),
- int(math.Round(fact * float64(sr.Y))),
- }
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/trig/trig.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/trig/trig.go
deleted file mode 100644
index 16d179d02..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/numbers/trig/trig.go
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package trig implements various trigonometrical calculations.
-package trig
-
-import (
- "fmt"
- "image"
- "math"
- "sort"
-
- "github.com/mum4k/termdash/private/numbers"
-)
-
-// CirclePointAtAngle given an angle in degrees and a circle midpoint and
-// radius, calculates coordinates of a point on the circle at that angle.
-// Angles are zero at the X axis and grow counter-clockwise.
-func CirclePointAtAngle(degrees int, mid image.Point, radius int) image.Point {
- angle := numbers.DegreesToRadians(degrees)
- r := float64(radius)
- x := mid.X + int(math.Round(r*math.Cos(angle)))
- // Y coordinates grow down on the canvas.
- y := mid.Y - int(math.Round(r*math.Sin(angle)))
- return image.Point{x, y}
-}
-
-// CircleAngleAtPoint given a point on a circle and its midpoint,
-// calculates the angle in degrees.
-// Angles are zero at the X axis and grow counter-clockwise.
-func CircleAngleAtPoint(point, mid image.Point) int {
- adj := float64(point.X - mid.X)
- opp := float64(mid.Y - point.Y)
- if opp != 0 {
- angle := math.Atan2(opp, adj)
- return numbers.RadiansToDegrees(angle)
- } else if adj >= 0 {
- return 0
- } else {
- return 180
- }
-}
-
-// PointIsIn asserts whether the provided point is inside of a shape outlined
-// with the provided points.
-// Does not verify that the shape is closed or complete, it merely counts the
-// number of intersections with the shape on one row.
-func PointIsIn(p image.Point, points []image.Point) bool {
- maxX := p.X
- set := map[image.Point]struct{}{}
- for _, sp := range points {
- set[sp] = struct{}{}
- if sp.X > maxX {
- maxX = sp.X
- }
- }
-
- if _, ok := set[p]; ok {
- // Not inside if it is on the shape.
- return false
- }
-
- byY := map[int][]int{} // maps y->x
- for p := range set {
- byY[p.Y] = append(byY[p.Y], p.X)
- }
- for y := range byY {
- sort.Ints(byY[y])
- }
-
- set = map[image.Point]struct{}{}
- for y, xses := range byY {
- set[image.Point{xses[0], y}] = struct{}{}
- if len(xses) == 1 {
- continue
- }
-
- for i := 1; i < len(xses); i++ {
- if xses[i] != xses[i-1]+1 {
- set[image.Point{xses[i], y}] = struct{}{}
- }
- }
- }
-
- crosses := 0
- for x := p.X; x <= maxX; x++ {
- if _, ok := set[image.Point{x, p.Y}]; ok {
- crosses++
- }
- }
- return crosses%2 != 0
-}
-
-const (
- // MinAngle is the smallest valid angle in degrees.
- MinAngle = 0
- // MaxAngle is the largest valid angle in degrees.
- MaxAngle = 360
-)
-
-// angleRange represents a range of angles in degrees.
-// The range includes all angles such that start <= angle <= end.
-type angleRange struct {
- // start is the start if the range.
- // This is always less or equal to the end.
- start int
-
- // end is the end of the range.
- end int
-}
-
-// contains asserts whether the specified angle is in the range.
-func (ar *angleRange) contains(angle int) bool {
- return angle >= ar.start && angle <= ar.end
-}
-
-// normalizeRange normalizes the start and end angles in degrees into ranges of
-// angles. Useful for cases where the 0/360 point falls within the range.
-// E.g:
-// 0,25 => angleRange{0, 26}
-// 0,360 => angleRange{0, 361}
-// 359,20 => angleRange{359, 361}, angleRange{0, 21}
-func normalizeRange(start, end int) ([]*angleRange, error) {
- if start < MinAngle || start > MaxAngle {
- return nil, fmt.Errorf("invalid start angle:%d, must be in range %d <= start <= %d", start, MinAngle, MaxAngle)
- }
- if end < MinAngle || end > MaxAngle {
- return nil, fmt.Errorf("invalid end angle:%d, must be in range %d <= end <= %d", end, MinAngle, MaxAngle)
- }
-
- if start == MaxAngle && end == 0 {
- start, end = end, start
- }
-
- if start <= end {
- return []*angleRange{
- {start, end},
- }, nil
- }
-
- // The range is crossing the 0/360 degree point.
- // Break it into multiple ranges.
- return []*angleRange{
- {start, MaxAngle},
- {0, end},
- }, nil
-}
-
-// RangeSize returns the size of the degree range.
-// E.g:
-// 0,25 => 25
-// 359,1 => 2
-func RangeSize(start, end int) (int, error) {
- ranges, err := normalizeRange(start, end)
- if err != nil {
- return 0, err
- }
- if len(ranges) == 1 {
- return end - start, nil
- }
- return MaxAngle - start + end, nil
-}
-
-// RangeMid returns an angle that lies in the middle between start and end.
-// E.g:
-// 0,10 => 5
-// 350,10 => 0
-func RangeMid(start, end int) (int, error) {
- ranges, err := normalizeRange(start, end)
- if err != nil {
- return 0, err
- }
- if len(ranges) == 1 {
- return start + ((end - start) / 2), nil
- }
-
- length := MaxAngle - start + end
- want := length / 2
- res := start + want
- return res % MaxAngle, nil
-}
-
-// FilterByAngle filters the provided points, returning only those that fall
-// within the starting and the ending angle on a circle with the provided mid
-// point.
-func FilterByAngle(points []image.Point, mid image.Point, start, end int) ([]image.Point, error) {
- var res []image.Point
- ranges, err := normalizeRange(start, end)
- if err != nil {
- return nil, err
- }
- if mid.X < 0 || mid.Y < 0 {
- return nil, fmt.Errorf("the mid point %v cannot have negative coordinates", mid)
- }
-
- for _, p := range points {
- angle := CircleAngleAtPoint(p, mid)
-
- // Edge case, this might mean 0 or 360.
- // Decide based on where we are starting.
- if angle == 0 && start > 0 {
- angle = MaxAngle
- }
-
- for _, r := range ranges {
- if r.contains(angle) {
- res = append(res, p)
- break
- }
- }
- }
- return res, nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/runewidth/runewidth.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/runewidth/runewidth.go
deleted file mode 100644
index 4f2f63a8f..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/runewidth/runewidth.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2019 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package runewidth is a wrapper over github.com/mattn/go-runewidth which
-// gives different treatment to certain runes with ambiguous width.
-package runewidth
-
-import runewidth "github.com/mattn/go-runewidth"
-
-// RuneWidth returns the number of cells needed to draw r.
-// Background in http://www.unicode.org/reports/tr11/.
-//
-// Treats runes used internally by termdash as single-cell (half-width) runes
-// regardless of the locale. I.e. runes that are used to draw lines, boxes,
-// indicate resize or text trimming was needed and runes used by the braille
-// canvas.
-//
-// This should be safe, since even in locales where these runes have ambiguous
-// width, we still place all the character content around them so they should
-// have be half-width.
-func RuneWidth(r rune) int {
- if inTable(r, exceptions) {
- return 1
- }
- return runewidth.RuneWidth(r)
-}
-
-// StringWidth is like RuneWidth, but returns the number of cells occupied by
-// all the runes in the string.
-func StringWidth(s string) int {
- var width int
- for _, r := range []rune(s) {
- width += RuneWidth(r)
- }
- return width
-}
-
-// inTable determines if the rune falls within the table.
-// Copied from github.com/mattn/go-runewidth/blob/master/runewidth.go.
-func inTable(r rune, t table) bool {
- // func (t table) IncludesRune(r rune) bool {
- if r < t[0].first {
- return false
- }
-
- bot := 0
- top := len(t) - 1
- for top >= bot {
- mid := (bot + top) >> 1
-
- switch {
- case t[mid].last < r:
- bot = mid + 1
- case t[mid].first > r:
- top = mid - 1
- default:
- return true
- }
- }
-
- return false
-}
-
-type interval struct {
- first rune
- last rune
-}
-
-type table []interval
-
-// exceptions runes defined here are always considered to be half-width even if
-// they might be ambiguous in some contexts.
-var exceptions = table{
- // Characters used by termdash to indicate text trim or scroll.
- {0x2026, 0x2026},
- {0x21c4, 0x21c4},
- {0x21e7, 0x21e7},
- {0x21e9, 0x21e9},
-
- // Box drawing, used as line-styles.
- // https://en.wikipedia.org/wiki/Box-drawing_character
- {0x2500, 0x257F},
-
- // Block elements used as sparks.
- // https://en.wikipedia.org/wiki/Box-drawing_character
- {0x2580, 0x258F},
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/private/wrap/wrap.go b/examples/go-dashboard/src/github.com/mum4k/termdash/private/wrap/wrap.go
deleted file mode 100644
index 5ee78a7a5..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/private/wrap/wrap.go
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package wrap implements line wrapping at character or word boundaries.
-package wrap
-
-import (
- "errors"
- "fmt"
- "strings"
- "unicode"
-
- "github.com/mum4k/termdash/private/canvas/buffer"
- "github.com/mum4k/termdash/private/runewidth"
-)
-
-// Mode sets the wrapping mode.
-type Mode int
-
-// String implements fmt.Stringer()
-func (m Mode) String() string {
- if n, ok := modeNames[m]; ok {
- return n
- }
- return "ModeUnknown"
-}
-
-// modeNames maps Mode values to human readable names.
-var modeNames = map[Mode]string{
- Never: "WrapModeNever",
- AtRunes: "WrapModeAtRunes",
- AtWords: "WrapModeAtWords",
-}
-
-const (
- // Never is the default wrapping mode, which disables line wrapping.
- Never Mode = iota
-
- // AtRunes is a wrapping mode where if the width of the text crosses the
- // width of the canvas, wrapping is performed at rune boundaries.
- AtRunes
-
- // AtWords is a wrapping mode where if the width of the text crosses the
- // width of the canvas, wrapping is performed at word boundaries. The
- // wrapping still switches back to the AtRunes mode for any words that are
- // longer than the width.
- AtWords
-)
-
-// ValidText validates the provided text for wrapping.
-// The text must not be empty, contain any control or
-// space characters other than '\n' and ' '.
-func ValidText(text string) error {
- if text == "" {
- return errors.New("the text cannot be empty")
- }
-
- for _, c := range text {
- if c == ' ' || c == '\n' { // Allowed space and control runes.
- continue
- }
- if unicode.IsControl(c) {
- return fmt.Errorf("the provided text %q cannot contain control characters, found: %q", text, c)
- }
- if unicode.IsSpace(c) {
- return fmt.Errorf("the provided text %q cannot contain space character %q", text, c)
- }
- }
- return nil
-}
-
-// ValidCells validates the provided cells for wrapping.
-// The text in the cells must follow the same rules as described for ValidText.
-func ValidCells(cells []*buffer.Cell) error {
- var b strings.Builder
- for _, c := range cells {
- b.WriteRune(c.Rune)
- }
- return ValidText(b.String())
-}
-
-// Cells returns the cells wrapped into individual lines according to the
-// specified width and wrapping mode.
-//
-// This function consumes any cells that contain newline characters and uses
-// them to start new lines.
-//
-// If the mode is AtWords, this function also drops cells with leading space
-// character before a word at which the wrap occurs.
-func Cells(cells []*buffer.Cell, width int, m Mode) ([][]*buffer.Cell, error) {
- if err := ValidCells(cells); err != nil {
- return nil, err
- }
- switch m {
- case Never:
- case AtRunes:
- case AtWords:
- default:
- return nil, fmt.Errorf("unsupported wrapping mode %v(%d)", m, m)
- }
- if width <= 0 {
- return nil, nil
- }
-
- cs := newCellScanner(cells, width, m)
- for state := scanCellRunes; state != nil; state = state(cs) {
- }
- return cs.lines, nil
-}
-
-// cellScannerState is a state in the FSM that scans the input text and identifies
-// newlines.
-type cellScannerState func(*cellScanner) cellScannerState
-
-// cellScanner tracks the progress of scanning the input cells when finding
-// lines.
-type cellScanner struct {
- // cells are the cells being scanned.
- cells []*buffer.Cell
-
- // nextIdx is the index of the cell that will be returned by next.
- nextIdx int
-
- // wordStartIdx stores the starting index of the current word.
- // A starting position of a word includes any leading space characters.
- // E.g.: hello world
- // ^
- // lastWordIdx
- wordStartIdx int
- // wordEndIdx stores the ending index of the current word.
- // The word consists of all indexes that are
- // wordStartIdx <= idx < wordEndIdx.
- // A word also includes any punctuation after it.
- wordEndIdx int
-
- // width is the width of the canvas the text will be drawn on.
- width int
-
- // posX tracks the horizontal position of the current cell on the canvas.
- posX int
-
- // mode is the wrapping mode.
- mode Mode
-
- // atRunesInWord overrides the mode back to AtRunes.
- atRunesInWord bool
-
- // lines are the identified lines.
- lines [][]*buffer.Cell
-
- // line is the current line.
- line []*buffer.Cell
-}
-
-// newCellScanner returns a scanner of the provided cells.
-func newCellScanner(cells []*buffer.Cell, width int, m Mode) *cellScanner {
- return &cellScanner{
- cells: cells,
- width: width,
- mode: m,
- }
-}
-
-// next returns the next cell and advances the scanner.
-// Returns nil when there are no more cells to scan.
-func (cs *cellScanner) next() *buffer.Cell {
- c := cs.peek()
- if c != nil {
- cs.nextIdx++
- }
- return c
-}
-
-// peek returns the next cell without advancing the scanner's position.
-// Returns nil when there are no more cells to peek at.
-func (cs *cellScanner) peek() *buffer.Cell {
- if cs.nextIdx >= len(cs.cells) {
- return nil
- }
- return cs.cells[cs.nextIdx]
-}
-
-// peekPrev returns the previous cell without changing the scanner's position.
-// Returns nil if the scanner is at the first cell.
-func (cs *cellScanner) peekPrev() *buffer.Cell {
- if cs.nextIdx == 0 {
- return nil
- }
- return cs.cells[cs.nextIdx-1]
-}
-
-// wordCells returns all the cells that belong to the current word.
-func (cs *cellScanner) wordCells() []*buffer.Cell {
- return cs.cells[cs.wordStartIdx:cs.wordEndIdx]
-}
-
-// wordWidth returns the width of the current word in cells when printed on the
-// terminal.
-func (cs *cellScanner) wordWidth() int {
- var b strings.Builder
- for _, wc := range cs.wordCells() {
- b.WriteRune(wc.Rune)
- }
- return runewidth.StringWidth(b.String())
-}
-
-// isWordStart determines if the scanner is at the beginning of a word.
-func (cs *cellScanner) isWordStart() bool {
- if cs.mode != AtWords {
- return false
- }
-
- current := cs.peekPrev()
- next := cs.peek()
- if current == nil || next == nil {
- return false
- }
-
- switch nr := next.Rune; {
- case nr == '\n':
- case nr == ' ':
- default:
- return true
- }
- return false
-}
-
-// scanCellRunes scans the cells a rune at a time.
-func scanCellRunes(cs *cellScanner) cellScannerState {
- for {
- cell := cs.next()
- if cell == nil {
- return scanEOF
- }
-
- r := cell.Rune
- if r == '\n' {
- return newLineForLineBreak
- }
-
- if cs.mode == Never {
- return runeToCurrentLine
- }
-
- if cs.atRunesInWord && !isWordCell(cell) {
- cs.atRunesInWord = false
- }
-
- if !cs.atRunesInWord && cs.isWordStart() {
- return markWordStart
- }
-
- if runeWrapNeeded(r, cs.posX, cs.width) {
- return newLineForAtRunes
- }
-
- return runeToCurrentLine
- }
-}
-
-// runeToCurrentLine scans a single cell rune onto the current line.
-func runeToCurrentLine(cs *cellScanner) cellScannerState {
- cell := cs.peekPrev()
- // Move horizontally within the line for each scanned cell.
- cs.posX += runewidth.RuneWidth(cell.Rune)
-
- // Copy the cell into the current line.
- cs.line = append(cs.line, cell)
- return scanCellRunes
-}
-
-// newLineForLineBreak processes a newline character cell.
-func newLineForLineBreak(cs *cellScanner) cellScannerState {
- cs.lines = append(cs.lines, cs.line)
- cs.posX = 0
- cs.line = nil
- return scanCellRunes
-}
-
-// newLineForAtRunes processes a line wrap at rune boundaries due to canvas width.
-func newLineForAtRunes(cs *cellScanner) cellScannerState {
- // The character on which we wrapped will be printed and is the start of
- // new line.
- cs.lines = append(cs.lines, cs.line)
- cs.posX = runewidth.RuneWidth(cs.peekPrev().Rune)
- cs.line = []*buffer.Cell{cs.peekPrev()}
- return scanCellRunes
-}
-
-// scanEOF terminates the scanning.
-func scanEOF(cs *cellScanner) cellScannerState {
- // Need to add the current line if it isn't empty, or if the previous rune
- // was a newline.
- // Newlines aren't copied onto the lines so just checking for emptiness
- // isn't enough. We still want to include trailing empty newlines if
- // they are in the input text.
- if len(cs.line) > 0 || cs.peekPrev().Rune == '\n' {
- cs.lines = append(cs.lines, cs.line)
- }
- return nil
-}
-
-// markWordStart stores the starting position of the current word.
-func markWordStart(cs *cellScanner) cellScannerState {
- cs.wordStartIdx = cs.nextIdx - 1
- cs.wordEndIdx = cs.nextIdx
- return scanWord
-}
-
-// scanWord scans the entire word until it finds its end.
-func scanWord(cs *cellScanner) cellScannerState {
- for {
- if isWordCell(cs.peek()) {
- cs.next()
- cs.wordEndIdx++
- continue
- }
- return wordToCurrentLine
- }
-}
-
-// wordToCurrentLine decides how to place the word into the output.
-func wordToCurrentLine(cs *cellScanner) cellScannerState {
- wordCells := cs.wordCells()
- wordWidth := cs.wordWidth()
-
- if cs.posX+wordWidth <= cs.width {
- // Place the word onto the current line.
- cs.posX += wordWidth
- cs.line = append(cs.line, wordCells...)
- return scanCellRunes
- }
- return wrapWord
-}
-
-// wrapWord wraps the word onto the next line or lines.
-func wrapWord(cs *cellScanner) cellScannerState {
- // Edge-case - the word starts the line and immediately doesn't fit.
- if cs.posX > 0 {
- cs.lines = append(cs.lines, cs.line)
- cs.posX = 0
- cs.line = nil
- }
-
- for i, wc := range cs.wordCells() {
- if i == 0 && wc.Rune == ' ' {
- // Skip the leading space when word wrapping.
- continue
- }
-
- if !runeWrapNeeded(wc.Rune, cs.posX, cs.width) {
- cs.posX += runewidth.RuneWidth(wc.Rune)
- cs.line = append(cs.line, wc)
- continue
- }
-
- // Replace the last placed rune with a dash indicating we wrapped the
- // word. Only do this for half-width runes.
- lastIdx := len(cs.line) - 1
- last := cs.line[lastIdx]
- lastRW := runewidth.RuneWidth(last.Rune)
- if cs.width > 1 && lastRW == 1 {
- cs.line[lastIdx] = buffer.NewCell('-', last.Opts)
- // Reset the scanner's position back to start scanning at the first
- // rune of this word that wasn't placed.
- cs.nextIdx = cs.wordStartIdx + i - 1
- } else {
- // Edge-case width is one, no space to put the dash rune.
- cs.nextIdx = cs.wordStartIdx + i
- }
- cs.atRunesInWord = true
- return scanCellRunes
- }
-
- cs.nextIdx = cs.wordEndIdx
- return scanCellRunes
-}
-
-// isWordCell determines if the cell contains a rune that belongs to a word.
-func isWordCell(c *buffer.Cell) bool {
- if c == nil {
- return false
- }
- switch r := c.Rune; {
- case r == '\n':
- case r == ' ':
- default:
- return true
- }
- return false
-}
-
-// runeWrapNeeded returns true if wrapping is needed for the rune at the horizontal
-// position on the canvas that has the specified width.
-func runeWrapNeeded(r rune, posX, width int) bool {
- rw := runewidth.RuneWidth(r)
- return posX > width-rw
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/termdash.go b/examples/go-dashboard/src/github.com/mum4k/termdash/termdash.go
deleted file mode 100644
index 8103ee717..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/termdash.go
+++ /dev/null
@@ -1,362 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-/*
-Package termdash implements a terminal based dashboard.
-
-While running, the terminal dashboard performs the following:
- - Periodic redrawing of the canvas and all the widgets.
- - Event based redrawing of the widgets (i.e. on Keyboard or Mouse events).
- - Forwards input events to widgets and optional subscribers.
- - Handles terminal resize events.
-*/
-package termdash
-
-import (
- "context"
- "errors"
- "fmt"
- "sync"
- "time"
-
- "github.com/mum4k/termdash/container"
- "github.com/mum4k/termdash/private/event"
- "github.com/mum4k/termdash/terminal/terminalapi"
-)
-
-// DefaultRedrawInterval is the default for the RedrawInterval option.
-const DefaultRedrawInterval = 250 * time.Millisecond
-
-// Option is used to provide options.
-type Option interface {
- // set sets the provided option.
- set(td *termdash)
-}
-
-// option implements Option.
-type option func(td *termdash)
-
-// set implements Option.set.
-func (o option) set(td *termdash) {
- o(td)
-}
-
-// RedrawInterval sets how often termdash redraws the container and all the widgets.
-// Defaults to DefaultRedrawInterval. Use the controller to disable the
-// periodic redraw.
-func RedrawInterval(t time.Duration) Option {
- return option(func(td *termdash) {
- td.redrawInterval = t
- })
-}
-
-// ErrorHandler is used to provide a function that will be called with all
-// errors that occur while the dashboard is running. If not provided, any
-// errors panic the application.
-// The provided function must be thread-safe.
-func ErrorHandler(f func(error)) Option {
- return option(func(td *termdash) {
- td.errorHandler = f
- })
-}
-
-// KeyboardSubscriber registers a subscriber for Keyboard events. Each
-// keyboard event is forwarded to the container and the registered subscriber.
-// The provided function must be thread-safe.
-func KeyboardSubscriber(f func(*terminalapi.Keyboard)) Option {
- return option(func(td *termdash) {
- td.keyboardSubscriber = f
- })
-}
-
-// MouseSubscriber registers a subscriber for Mouse events. Each mouse event
-// is forwarded to the container and the registered subscriber.
-// The provided function must be thread-safe.
-func MouseSubscriber(f func(*terminalapi.Mouse)) Option {
- return option(func(td *termdash) {
- td.mouseSubscriber = f
- })
-}
-
-// withEDS indicates that termdash should run with the provided event
-// distribution system instead of creating one.
-// Useful for tests.
-func withEDS(eds *event.DistributionSystem) Option {
- return option(func(td *termdash) {
- td.eds = eds
- })
-}
-
-// Run runs the terminal dashboard with the provided container on the terminal.
-// Redraws the terminal periodically. If you prefer a manual redraw, use the
-// Controller instead.
-// Blocks until the context expires.
-func Run(ctx context.Context, t terminalapi.Terminal, c *container.Container, opts ...Option) error {
- td := newTermdash(t, c, opts...)
-
- err := td.start(ctx)
- // Only return the status (error or nil) after the termdash event
- // processing goroutine actually exits.
- td.stop()
- return err
-}
-
-// Controller controls a termdash instance.
-// The controller instance is only valid until Close() is called.
-// The controller is not thread-safe.
-type Controller struct {
- td *termdash
- cancel context.CancelFunc
-}
-
-// NewController initializes termdash and returns an instance of the controller.
-// Periodic redrawing is disabled when using the controller, the RedrawInterval
-// option is ignored.
-// Close the controller when it isn't needed anymore.
-func NewController(t terminalapi.Terminal, c *container.Container, opts ...Option) (*Controller, error) {
- ctx, cancel := context.WithCancel(context.Background())
- ctrl := &Controller{
- td: newTermdash(t, c, opts...),
- cancel: cancel,
- }
-
- // stops when Close() is called.
- go ctrl.td.processEvents(ctx)
- if err := ctrl.td.periodicRedraw(); err != nil {
- return nil, err
- }
- return ctrl, nil
-}
-
-// Redraw triggers redraw of the terminal.
-func (c *Controller) Redraw() error {
- if c.td == nil {
- return errors.New("the termdash instance is no longer running, this controller is now invalid")
- }
-
- c.td.mu.Lock()
- defer c.td.mu.Unlock()
- return c.td.redraw()
-}
-
-// Close closes the Controller and its termdash instance.
-func (c *Controller) Close() {
- c.cancel()
- c.td.stop()
- c.td = nil
-}
-
-// termdash is a terminal based dashboard.
-// This object is thread-safe.
-type termdash struct {
- // term is the terminal the dashboard runs on.
- term terminalapi.Terminal
-
- // container maintains terminal splits and places widgets.
- container *container.Container
-
- // eds distributes input events to subscribers.
- eds *event.DistributionSystem
-
- // closeCh gets closed when Stop() is called, which tells the event
- // collecting goroutine to exit.
- closeCh chan struct{}
- // exitCh gets closed when the event collecting goroutine actually exits.
- exitCh chan struct{}
-
- // clearNeeded indicates if the terminal needs to be cleared next time
- // we're drawing it. Terminal needs to be cleared if its sized changed.
- clearNeeded bool
-
- // mu protects termdash.
- mu sync.Mutex
-
- // Options.
- redrawInterval time.Duration
- errorHandler func(error)
- mouseSubscriber func(*terminalapi.Mouse)
- keyboardSubscriber func(*terminalapi.Keyboard)
-}
-
-// newTermdash creates a new termdash.
-func newTermdash(t terminalapi.Terminal, c *container.Container, opts ...Option) *termdash {
- td := &termdash{
- term: t,
- container: c,
- eds: event.NewDistributionSystem(),
- closeCh: make(chan struct{}),
- exitCh: make(chan struct{}),
- redrawInterval: DefaultRedrawInterval,
- }
-
- for _, opt := range opts {
- opt.set(td)
- }
- td.subscribers()
- c.Subscribe(td.eds)
- return td
-}
-
-// subscribers subscribes event receivers that live in this package to EDS.
-func (td *termdash) subscribers() {
- // Handler for all errors that occur during input event processing.
- td.eds.Subscribe([]terminalapi.Event{terminalapi.NewError("")}, func(ev terminalapi.Event) {
- td.handleError(ev.(*terminalapi.Error).Error())
- })
-
- // Handles terminal resize events.
- td.eds.Subscribe([]terminalapi.Event{&terminalapi.Resize{}}, func(terminalapi.Event) {
- td.setClearNeeded()
- })
-
- // Redraws the screen on Keyboard and Mouse events.
- // These events very likely change the content of the widgets (e.g. zooming
- // a LineChart) so a redraw is needed to make that visible.
- td.eds.Subscribe([]terminalapi.Event{
- &terminalapi.Keyboard{},
- &terminalapi.Mouse{},
- }, func(terminalapi.Event) {
- td.evRedraw()
- }, event.MaxRepetitive(0)) // No repetitive events that cause terminal redraw.
-
- // Keyboard and Mouse subscribers specified via options.
- if td.keyboardSubscriber != nil {
- td.eds.Subscribe([]terminalapi.Event{&terminalapi.Keyboard{}}, func(ev terminalapi.Event) {
- td.keyboardSubscriber(ev.(*terminalapi.Keyboard))
- })
- }
- if td.mouseSubscriber != nil {
- td.eds.Subscribe([]terminalapi.Event{&terminalapi.Mouse{}}, func(ev terminalapi.Event) {
- td.mouseSubscriber(ev.(*terminalapi.Mouse))
- })
- }
-}
-
-// handleError forwards the error to the error handler if one was
-// provided or panics.
-func (td *termdash) handleError(err error) {
- if td.errorHandler != nil {
- td.errorHandler(err)
- } else {
- panic(err)
- }
-}
-
-// setClearNeeded flags that the terminal needs to be cleared next time we're
-// drawing it.
-func (td *termdash) setClearNeeded() {
- td.mu.Lock()
- defer td.mu.Unlock()
- td.clearNeeded = true
-}
-
-// redraw redraws the container and its widgets.
-// The caller must hold td.mu.
-func (td *termdash) redraw() error {
- if td.clearNeeded {
- if err := td.term.Clear(); err != nil {
- return fmt.Errorf("term.Clear => error: %v", err)
- }
- td.clearNeeded = false
- }
-
- if err := td.container.Draw(); err != nil {
- return fmt.Errorf("container.Draw => error: %v", err)
- }
-
- if err := td.term.Flush(); err != nil {
- return fmt.Errorf("term.Flush => error: %v", err)
- }
- return nil
-}
-
-// evRedraw redraws the container and its widgets.
-func (td *termdash) evRedraw() error {
- td.mu.Lock()
- defer td.mu.Unlock()
-
- // Don't redraw immediately, give widgets that are performing enough time
- // to update.
- // We don't want to actually synchronize until all widgets update, we are
- // purposefully leaving slow widgets behind.
- time.Sleep(25 * time.Millisecond)
- return td.redraw()
-}
-
-// periodicRedraw is called once each RedrawInterval.
-func (td *termdash) periodicRedraw() error {
- td.mu.Lock()
- defer td.mu.Unlock()
- return td.redraw()
-}
-
-// processEvents processes terminal input events.
-// This is the body of the event collecting goroutine.
-func (td *termdash) processEvents(ctx context.Context) {
- defer close(td.exitCh)
-
- for {
- ev := td.term.Event(ctx)
- if ev != nil {
- td.eds.Event(ev)
- }
-
- select {
- case <-ctx.Done():
- return
- default:
- }
- }
-}
-
-// start starts the terminal dashboard. Blocks until the context expires or
-// until stop() is called.
-func (td *termdash) start(ctx context.Context) error {
- // Redraw once to initialize the container sizes.
- if err := td.periodicRedraw(); err != nil {
- close(td.exitCh)
- return err
- }
-
- redrawTimer := time.NewTicker(td.redrawInterval)
- defer redrawTimer.Stop()
-
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
-
- // stops when stop() is called or the context expires.
- go td.processEvents(ctx)
-
- for {
- select {
- case <-redrawTimer.C:
- if err := td.periodicRedraw(); err != nil {
- return err
- }
-
- case <-ctx.Done():
- return nil
-
- case <-td.closeCh:
- return nil
- }
- }
-}
-
-// stop tells the event collecting goroutine to stop.
-// Blocks until it exits.
-func (td *termdash) stop() {
- close(td.closeCh)
- <-td.exitCh
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/cell_options.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/cell_options.go
deleted file mode 100644
index 41ee76013..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/cell_options.go
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package termbox
-
-// cell_options.go converts termdash cell options to the termbox format.
-
-import (
- "github.com/mum4k/termdash/cell"
- tbx "github.com/nsf/termbox-go"
-)
-
-// cellColor converts termdash cell color to the termbox format.
-func cellColor(c cell.Color) tbx.Attribute {
- return tbx.Attribute(c)
-}
-
-// cellOptsToFg converts the cell options to the termbox foreground attribute.
-func cellOptsToFg(opts *cell.Options) tbx.Attribute {
- return cellColor(opts.FgColor)
-}
-
-// cellOptsToBg converts the cell options to the termbox background attribute.
-func cellOptsToBg(opts *cell.Options) tbx.Attribute {
- return cellColor(opts.BgColor)
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/color_mode.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/color_mode.go
deleted file mode 100644
index 793f2a966..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/color_mode.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package termbox
-
-import (
- "fmt"
-
- "github.com/mum4k/termdash/terminal/terminalapi"
- tbx "github.com/nsf/termbox-go"
-)
-
-// colorMode converts termdash color modes to the termbox format.
-func colorMode(cm terminalapi.ColorMode) (tbx.OutputMode, error) {
- switch cm {
- case terminalapi.ColorModeNormal:
- return tbx.OutputNormal, nil
- case terminalapi.ColorMode256:
- return tbx.Output256, nil
- case terminalapi.ColorMode216:
- return tbx.Output216, nil
- case terminalapi.ColorModeGrayscale:
- return tbx.OutputGrayscale, nil
- default:
- return -1, fmt.Errorf("don't know how to convert color mode %v to the termbox format", cm)
- }
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/event.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/event.go
deleted file mode 100644
index c26d88c18..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/event.go
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package termbox
-
-// event.go converts termbox events to the termdash format.
-
-import (
- "image"
-
- "github.com/mum4k/termdash/keyboard"
- "github.com/mum4k/termdash/mouse"
- "github.com/mum4k/termdash/terminal/terminalapi"
- tbx "github.com/nsf/termbox-go"
-)
-
-// tbxToTd maps termbox key values to the termdash format.
-var tbxToTd = map[tbx.Key]keyboard.Key{
- tbx.KeySpace: keyboard.KeySpace,
- tbx.KeyF1: keyboard.KeyF1,
- tbx.KeyF2: keyboard.KeyF2,
- tbx.KeyF3: keyboard.KeyF3,
- tbx.KeyF4: keyboard.KeyF4,
- tbx.KeyF5: keyboard.KeyF5,
- tbx.KeyF6: keyboard.KeyF6,
- tbx.KeyF7: keyboard.KeyF7,
- tbx.KeyF8: keyboard.KeyF8,
- tbx.KeyF9: keyboard.KeyF9,
- tbx.KeyF10: keyboard.KeyF10,
- tbx.KeyF11: keyboard.KeyF11,
- tbx.KeyF12: keyboard.KeyF12,
- tbx.KeyInsert: keyboard.KeyInsert,
- tbx.KeyDelete: keyboard.KeyDelete,
- tbx.KeyHome: keyboard.KeyHome,
- tbx.KeyEnd: keyboard.KeyEnd,
- tbx.KeyPgup: keyboard.KeyPgUp,
- tbx.KeyPgdn: keyboard.KeyPgDn,
- tbx.KeyArrowUp: keyboard.KeyArrowUp,
- tbx.KeyArrowDown: keyboard.KeyArrowDown,
- tbx.KeyArrowLeft: keyboard.KeyArrowLeft,
- tbx.KeyArrowRight: keyboard.KeyArrowRight,
- tbx.KeyCtrlTilde: keyboard.KeyCtrlTilde,
- tbx.KeyCtrlA: keyboard.KeyCtrlA,
- tbx.KeyCtrlB: keyboard.KeyCtrlB,
- tbx.KeyCtrlC: keyboard.KeyCtrlC,
- tbx.KeyCtrlD: keyboard.KeyCtrlD,
- tbx.KeyCtrlE: keyboard.KeyCtrlE,
- tbx.KeyCtrlF: keyboard.KeyCtrlF,
- tbx.KeyCtrlG: keyboard.KeyCtrlG,
- tbx.KeyBackspace: keyboard.KeyBackspace,
- tbx.KeyTab: keyboard.KeyTab,
- tbx.KeyCtrlJ: keyboard.KeyCtrlJ,
- tbx.KeyCtrlK: keyboard.KeyCtrlK,
- tbx.KeyCtrlL: keyboard.KeyCtrlL,
- tbx.KeyEnter: keyboard.KeyEnter,
- tbx.KeyCtrlN: keyboard.KeyCtrlN,
- tbx.KeyCtrlO: keyboard.KeyCtrlO,
- tbx.KeyCtrlP: keyboard.KeyCtrlP,
- tbx.KeyCtrlQ: keyboard.KeyCtrlQ,
- tbx.KeyCtrlR: keyboard.KeyCtrlR,
- tbx.KeyCtrlS: keyboard.KeyCtrlS,
- tbx.KeyCtrlT: keyboard.KeyCtrlT,
- tbx.KeyCtrlU: keyboard.KeyCtrlU,
- tbx.KeyCtrlV: keyboard.KeyCtrlV,
- tbx.KeyCtrlW: keyboard.KeyCtrlW,
- tbx.KeyCtrlX: keyboard.KeyCtrlX,
- tbx.KeyCtrlY: keyboard.KeyCtrlY,
- tbx.KeyCtrlZ: keyboard.KeyCtrlZ,
- tbx.KeyEsc: keyboard.KeyEsc,
- tbx.KeyCtrl4: keyboard.KeyCtrl4,
- tbx.KeyCtrl5: keyboard.KeyCtrl5,
- tbx.KeyCtrl6: keyboard.KeyCtrl6,
- tbx.KeyCtrl7: keyboard.KeyCtrl7,
- tbx.KeyBackspace2: keyboard.KeyBackspace2,
-}
-
-// convKey converts a termbox keyboard event to the termdash format.
-func convKey(tbxEv tbx.Event) terminalapi.Event {
- if tbxEv.Key != 0 && tbxEv.Ch != 0 {
- return terminalapi.NewErrorf("the key event contain both a key(%v) and a character(%v)", tbxEv.Key, tbxEv.Ch)
- }
-
- if tbxEv.Ch != 0 {
- return &terminalapi.Keyboard{
- Key: keyboard.Key(tbxEv.Ch),
- }
- }
-
- k, ok := tbxToTd[tbxEv.Key]
- if !ok {
- return terminalapi.NewErrorf("unknown keyboard key '%v' in a keyboard event", k)
- }
- return &terminalapi.Keyboard{
- Key: k,
- }
-}
-
-// convMouse converts a termbox mouse event to the termdash format.
-func convMouse(tbxEv tbx.Event) terminalapi.Event {
- var button mouse.Button
-
- switch k := tbxEv.Key; k {
- case tbx.MouseLeft:
- button = mouse.ButtonLeft
- case tbx.MouseMiddle:
- button = mouse.ButtonMiddle
- case tbx.MouseRight:
- button = mouse.ButtonRight
- case tbx.MouseRelease:
- button = mouse.ButtonRelease
- case tbx.MouseWheelUp:
- button = mouse.ButtonWheelUp
- case tbx.MouseWheelDown:
- button = mouse.ButtonWheelDown
- default:
- return terminalapi.NewErrorf("unknown mouse key %v in a mouse event", k)
- }
-
- return &terminalapi.Mouse{
- Position: image.Point{tbxEv.MouseX, tbxEv.MouseY},
- Button: button,
- }
-}
-
-// convResize converts a termbox resize event to the termdash format.
-func convResize(tbxEv tbx.Event) terminalapi.Event {
- size := image.Point{tbxEv.Width, tbxEv.Height}
- if size.X < 0 || size.Y < 0 {
- return terminalapi.NewErrorf("terminal resized to negative size: %v", size)
- }
- return &terminalapi.Resize{
- Size: size,
- }
-}
-
-// toTermdashEvents converts a termbox event to the termdash event format.
-func toTermdashEvents(tbxEv tbx.Event) []terminalapi.Event {
- switch t := tbxEv.Type; t {
- case tbx.EventInterrupt:
- return []terminalapi.Event{
- terminalapi.NewError("event type EventInterrupt isn't supported"),
- }
- case tbx.EventRaw:
- return []terminalapi.Event{
- terminalapi.NewError("event type EventRaw isn't supported"),
- }
- case tbx.EventNone:
- return []terminalapi.Event{
- terminalapi.NewError("event type EventNone isn't supported"),
- }
- case tbx.EventError:
- return []terminalapi.Event{
- terminalapi.NewErrorf("input error occurred: %v", tbxEv.Err),
- }
- case tbx.EventResize:
- return []terminalapi.Event{convResize(tbxEv)}
- case tbx.EventMouse:
- return []terminalapi.Event{convMouse(tbxEv)}
- case tbx.EventKey:
- return []terminalapi.Event{
- convKey(tbxEv),
- }
- default:
- return []terminalapi.Event{
- terminalapi.NewErrorf("unknown termbox event type: %v", t),
- }
- }
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/termbox.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/termbox.go
deleted file mode 100644
index 4329e4686..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/termbox/termbox.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package termbox implements terminal using the nsf/termbox-go library.
-package termbox
-
-import (
- "context"
- "image"
-
- "github.com/mum4k/termdash/cell"
- "github.com/mum4k/termdash/private/event/eventqueue"
- "github.com/mum4k/termdash/terminal/terminalapi"
- tbx "github.com/nsf/termbox-go"
-)
-
-// Option is used to provide options.
-type Option interface {
- // set sets the provided option.
- set(*Terminal)
-}
-
-// option implements Option.
-type option func(*Terminal)
-
-// set implements Option.set.
-func (o option) set(t *Terminal) {
- o(t)
-}
-
-// DefaultColorMode is the default value for the ColorMode option.
-const DefaultColorMode = terminalapi.ColorMode256
-
-// ColorMode sets the terminal color mode.
-// Defaults to DefaultColorMode.
-func ColorMode(cm terminalapi.ColorMode) Option {
- return option(func(t *Terminal) {
- t.colorMode = cm
- })
-}
-
-// Terminal provides input and output to a real terminal. Wraps the
-// nsf/termbox-go terminal implementation. This object is not thread-safe.
-// Implements terminalapi.Terminal.
-type Terminal struct {
- // events is a queue of input events.
- events *eventqueue.Unbound
-
- // done gets closed when Close() is called.
- done chan struct{}
-
- // Options.
- colorMode terminalapi.ColorMode
-}
-
-// newTerminal creates the terminal and applies the options.
-func newTerminal(opts ...Option) *Terminal {
- t := &Terminal{
- events: eventqueue.New(),
- done: make(chan struct{}),
- colorMode: DefaultColorMode,
- }
- for _, opt := range opts {
- opt.set(t)
- }
- return t
-}
-
-// New returns a new termbox based Terminal.
-// Call Close() when the terminal isn't required anymore.
-func New(opts ...Option) (*Terminal, error) {
- if err := tbx.Init(); err != nil {
- return nil, err
- }
- tbx.SetInputMode(tbx.InputEsc | tbx.InputMouse)
-
- t := newTerminal(opts...)
- om, err := colorMode(t.colorMode)
- if err != nil {
- return nil, err
- }
- tbx.SetOutputMode(om)
-
- go t.pollEvents() // Stops when Close() is called.
- return t, nil
-}
-
-// Size implements terminalapi.Terminal.Size.
-func (t *Terminal) Size() image.Point {
- w, h := tbx.Size()
- return image.Point{w, h}
-}
-
-// Clear implements terminalapi.Terminal.Clear.
-func (t *Terminal) Clear(opts ...cell.Option) error {
- o := cell.NewOptions(opts...)
- return tbx.Clear(cellOptsToFg(o), cellOptsToBg(o))
-}
-
-// Flush implements terminalapi.Terminal.Flush.
-func (t *Terminal) Flush() error {
- return tbx.Flush()
-}
-
-// SetCursor implements terminalapi.Terminal.SetCursor.
-func (t *Terminal) SetCursor(p image.Point) {
- tbx.SetCursor(p.X, p.Y)
-}
-
-// HideCursor implements terminalapi.Terminal.HideCursor.
-func (t *Terminal) HideCursor() {
- tbx.HideCursor()
-}
-
-// SetCell implements terminalapi.Terminal.SetCell.
-func (t *Terminal) SetCell(p image.Point, r rune, opts ...cell.Option) error {
- o := cell.NewOptions(opts...)
- tbx.SetCell(p.X, p.Y, r, cellOptsToFg(o), cellOptsToBg(o))
- return nil
-}
-
-// pollEvents polls and enqueues the input events.
-func (t *Terminal) pollEvents() {
- for {
- select {
- case <-t.done:
- return
- default:
- }
-
- events := toTermdashEvents(tbx.PollEvent())
- for _, ev := range events {
- t.events.Push(ev)
- }
- }
-}
-
-// Event implements terminalapi.Terminal.Event.
-func (t *Terminal) Event(ctx context.Context) terminalapi.Event {
- ev := t.events.Pull(ctx)
- if ev == nil {
- return nil
- }
- return ev
-}
-
-// Close closes the terminal, should be called when the terminal isn't required
-// anymore to return the screen to a sane state.
-// Implements terminalapi.Terminal.Close.
-func (t *Terminal) Close() {
- close(t.done)
- tbx.Close()
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/color_mode.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/color_mode.go
deleted file mode 100644
index 28c93277f..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/color_mode.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package terminalapi
-
-// color_mode.go defines the terminal color modes.
-
-// ColorMode represents a color mode of a terminal.
-type ColorMode int
-
-// String implements fmt.Stringer()
-func (cm ColorMode) String() string {
- if n, ok := colorModeNames[cm]; ok {
- return n
- }
- return "ColorModeUnknown"
-}
-
-// colorModeNames maps ColorMode values to human readable names.
-var colorModeNames = map[ColorMode]string{
- ColorModeNormal: "ColorModeNormal",
- ColorMode256: "ColorMode256",
- ColorMode216: "ColorMode216",
- ColorModeGrayscale: "ColorModeGrayscale",
-}
-
-// Supported color modes.
-const (
- // ColorModeNormal supports 8 "system" colors.
- // These are defined as constants in the cell package.
- ColorModeNormal ColorMode = iota
-
- // ColorMode256 enables using any of the 256 terminal colors.
- // 0-7: the 8 "system" colors accessible in ColorModeNormal.
- // 8-15: the 8 "bright system" colors.
- // 16-231: the 216 different terminal colors.
- // 232-255: the 24 different shades of grey.
- ColorMode256
-
- // ColorMode216 supports only the third range of the ColorMode256, i.e the
- // 216 different terminal colors. However in this mode the colors are zero
- // based, so the caller doesn't need to provide an offset.
- ColorMode216
-
- // ColorModeGrayscale supports only the fourth range of the ColorMode256,
- // i.e the 24 different shades of grey. However in this mode the colors are
- // zero based, so the caller doesn't need to provide an offset.
- ColorModeGrayscale
-)
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/event.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/event.go
deleted file mode 100644
index a543e8433..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/event.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package terminalapi
-
-import (
- "errors"
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/keyboard"
- "github.com/mum4k/termdash/mouse"
-)
-
-// event.go defines events that can be received through the terminal API.
-
-// Event represents an input event.
-type Event interface {
- isEvent()
-}
-
-// Keyboard is the event used when a key is pressed.
-// Implements terminalapi.Event.
-type Keyboard struct {
- // Key is the pressed key.
- Key keyboard.Key
-}
-
-func (*Keyboard) isEvent() {}
-
-// String implements fmt.Stringer.
-func (k Keyboard) String() string {
- return fmt.Sprintf("Keyboard{Key: %v}", k.Key)
-}
-
-// Resize is the event used when the terminal was resized.
-// Implements terminalapi.Event.
-type Resize struct {
- // Size is the new size of the terminal.
- Size image.Point
-}
-
-func (*Resize) isEvent() {}
-
-// String implements fmt.Stringer.
-func (r Resize) String() string {
- return fmt.Sprintf("Resize{Size: %v}", r.Size)
-}
-
-// Mouse is the event used when the mouse is moved or a mouse button is
-// pressed.
-// Implements terminalapi.Event.
-type Mouse struct {
- // Position of the mouse on the terminal.
- Position image.Point
- // Button identifies the pressed button if any.
- Button mouse.Button
-}
-
-func (*Mouse) isEvent() {}
-
-// String implements fmt.Stringer.
-func (m Mouse) String() string {
- return fmt.Sprintf("Mouse{Position: %v, Button: %v}", m.Position, m.Button)
-}
-
-// Error is an event indicating an error while processing input.
-type Error string
-
-// NewError returns a new Error event.
-func NewError(e string) *Error {
- err := Error(e)
- return &err
-}
-
-// NewErrorf returns a new Error event, arguments are similar to fmt.Sprintf.
-func NewErrorf(format string, args ...interface{}) *Error {
- err := Error(fmt.Sprintf(format, args...))
- return &err
-}
-
-func (*Error) isEvent() {}
-
-// Error returns the error that occurred.
-func (e *Error) Error() error {
- if e == nil || *e == "" {
- return nil
- }
- return errors.New(string(*e))
-}
-
-// String implements fmt.Stringer.
-func (e Error) String() string {
- return string(e)
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/terminalapi.go b/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/terminalapi.go
deleted file mode 100644
index 831abc169..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/terminal/terminalapi/terminalapi.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package terminalapi defines the API of all terminal implementations.
-package terminalapi
-
-import (
- "context"
- "image"
-
- "github.com/mum4k/termdash/cell"
-)
-
-// Terminal abstracts an implementation of a 2-D terminal.
-// A terminal consists of a number of cells.
-type Terminal interface {
- // Size returns the terminal width and height in cells.
- Size() image.Point
-
- // Clear clears the content of the internal back buffer, resetting all
- // cells to their default content and attributes. Sets the provided options
- // on all the cell.
- Clear(opts ...cell.Option) error
- // Flush flushes the internal back buffer to the terminal.
- Flush() error
-
- // SetCursor sets the position of the cursor.
- SetCursor(p image.Point)
- // HideCursos hides the cursor.
- HideCursor()
-
- // SetCell sets the value of the specified cell to the provided rune.
- // Use the options to specify which attributes to modify, if an attribute
- // option isn't specified, the attribute retains its previous value.
- SetCell(p image.Point, r rune, opts ...cell.Option) error
-
- // Event waits for the next event and returns it.
- // This call blocks until the next event or cancellation of the context.
- // Returns nil when the context gets canceled.
- Event(ctx context.Context) Event
-
- // Close closes the underlying terminal implementation and should be called when
- // the terminal isn't required anymore to return the screen to a sane state.
- Close()
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgetapi/widgetapi.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgetapi/widgetapi.go
deleted file mode 100644
index ee27136a1..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgetapi/widgetapi.go
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package widgetapi defines the API of a widget on the dashboard.
-package widgetapi
-
-import (
- "image"
-
- "github.com/mum4k/termdash/private/canvas"
- "github.com/mum4k/termdash/terminal/terminalapi"
-)
-
-// KeyScope indicates the scope at which the widget wants to receive keyboard
-// events.
-type KeyScope int
-
-// String implements fmt.Stringer()
-func (ks KeyScope) String() string {
- if n, ok := keyScopeNames[ks]; ok {
- return n
- }
- return "KeyScopeUnknown"
-}
-
-// keyScopeNames maps KeyScope values to human readable names.
-var keyScopeNames = map[KeyScope]string{
- KeyScopeNone: "KeyScopeNone",
- KeyScopeFocused: "KeyScopeFocused",
- KeyScopeGlobal: "KeyScopeGlobal",
-}
-
-const (
- // KeyScopeNone is used when the widget doesn't want to receive any
- // keyboard events.
- KeyScopeNone KeyScope = iota
-
- // KeyScopeFocused is used when the widget wants to only receive keyboard
- // events when its container is focused.
- KeyScopeFocused
-
- // KeyScopeGlobal is used when the widget wants to receive all keyboard
- // events regardless of which container is focused.
- KeyScopeGlobal
-)
-
-// MouseScope indicates the scope at which the widget wants to receive mouse
-// events.
-type MouseScope int
-
-// String implements fmt.Stringer()
-func (ms MouseScope) String() string {
- if n, ok := mouseScopeNames[ms]; ok {
- return n
- }
- return "MouseScopeUnknown"
-}
-
-// mouseScopeNames maps MouseScope values to human readable names.
-var mouseScopeNames = map[MouseScope]string{
- MouseScopeNone: "MouseScopeNone",
- MouseScopeWidget: "MouseScopeWidget",
- MouseScopeContainer: "MouseScopeContainer",
- MouseScopeGlobal: "MouseScopeGlobal",
-}
-
-const (
- // MouseScopeNone is used when the widget doesn't want to receive any mouse
- // events.
- MouseScopeNone MouseScope = iota
-
- // MouseScopeWidget is used when the widget only wants mouse events that
- // fall onto its canvas.
- // The position of these widgets is always relative to widget's canvas.
- MouseScopeWidget
-
- // MouseScopeContainer is used when the widget only wants mouse events that
- // fall onto its container. The area size of a container is always larger
- // or equal to the one of the widget's canvas. So a widget selecting
- // MouseScopeContainer will either receive the same or larger amount of
- // events as compared to MouseScopeWidget.
- // The position of mouse events that fall outside of widget's canvas is
- // reset to image.Point{-1, -1}.
- // The widgets are allowed to process the button event.
- MouseScopeContainer
-
- // MouseScopeGlobal is used when the widget wants to receive all mouse
- // events regardless on where on the terminal they land.
- // The position of mouse events that fall outside of widget's canvas is
- // reset to image.Point{-1, -1} and must not be used by the widgets.
- // The widgets are allowed to process the button event.
- MouseScopeGlobal
-)
-
-// Options contains registration options for a widget.
-// This is how the widget indicates its needs to the infrastructure.
-type Options struct {
- // Ratio allows a widget to request a canvas whose size will always have
- // the specified ratio of width:height (Ratio.X:Ratio.Y).
- // The zero value i.e. image.Point{0, 0} indicates that the widget accepts
- // canvas of any ratio.
- Ratio image.Point
-
- // MinimumSize allows a widget to specify the smallest allowed canvas size.
- // If the terminal size and/or splits cause the assigned canvas to be
- // smaller than this, the widget will be skipped. I.e. The Draw() method
- // won't be called until a resize above the specified minimum.
- MinimumSize image.Point
-
- // MaximumSize allows a widget to specify the largest allowed canvas size.
- // If the terminal size and/or splits cause the assigned canvas to be larger
- // than this, the widget will only receive a canvas of this size within its
- // container. Setting any of the two coordinates to zero indicates
- // unlimited.
- MaximumSize image.Point
-
- // WantKeyboard allows a widget to request keyboard events and specify
- // their desired scope. If set to KeyScopeNone, no keyboard events are
- // forwarded to the widget.
- WantKeyboard KeyScope
-
- // WantMouse allows a widget to request mouse events and specify their
- // desired scope. If set to MouseScopeNone, no mouse events are forwarded
- // to the widget.
- // Note that the widget is only able to see the position of the mouse event
- // if it falls onto its canvas. See the documentation next to individual
- // MouseScope values for details.
- WantMouse MouseScope
-}
-
-// Meta provide additional metadata to widgets.
-type Meta struct {
- // Focused asserts whether the widget's container is focused.
- Focused bool
-}
-
-// Widget is a single widget on the dashboard.
-// Implementations must be thread safe.
-type Widget interface {
- // When the infrastructure calls Draw(), the widget must block on the call
- // until it finishes drawing onto the provided canvas. When given the
- // canvas, the widget must first determine its size by calling
- // Canvas.Size(), then limit all its drawing to this area.
- //
- // The widget must not assume that the size of the canvas or its content
- // remains the same between calls.
- //
- // The argument meta is guaranteed to be valid (i.e. non-nil).
- Draw(cvs *canvas.Canvas, meta *Meta) error
-
- // Keyboard is called when the widget is focused on the dashboard and a key
- // shortcut the widget registered for was pressed. Only called if the widget
- // registered for keyboard events.
- Keyboard(k *terminalapi.Keyboard) error
-
- // Mouse is called when the widget is focused on the dashboard and a mouse
- // event happens on its canvas. Only called if the widget registered for mouse
- // events.
- Mouse(m *terminalapi.Mouse) error
-
- // Options returns registration options for the widget.
- // This is how the widget indicates to the infrastructure whether it is
- // interested in keyboard or mouse shortcuts, what is its minimum canvas
- // size, etc.
- //
- // Most widgets will return statically compiled options (minimum and
- // maximum size, etc.). If the returned options depend on the runtime state
- // of the widget (e.g. the user data provided to the widget), the widget
- // must protect against a case where the infrastructure calls the Draw
- // method with a canvas that doesn't meet the requested options. This is
- // because the data in the widget might change between calls to Options and
- // Draw.
- Options() Options
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/line_trim.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/line_trim.go
deleted file mode 100644
index 6ca8c83aa..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/line_trim.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package text
-
-import (
- "fmt"
- "image"
-
- "github.com/mum4k/termdash/private/canvas"
- "github.com/mum4k/termdash/private/runewidth"
- "github.com/mum4k/termdash/private/wrap"
-)
-
-// line_trim.go contains code that trims lines that are too long.
-
-type trimResult struct {
- // trimmed is set to true if the current and the following runes on this
- // line are trimmed.
- trimmed bool
-
- // curPoint is the updated current point the drawing should continue on.
- curPoint image.Point
-}
-
-// drawTrimChar draws the horizontal ellipsis '…' character as the last
-// character in the canvas on the specified line.
-func drawTrimChar(cvs *canvas.Canvas, line int) error {
- lastPoint := image.Point{cvs.Area().Dx() - 1, line}
- // If the penultimate cell contains a full-width rune, we need to clear it
- // first. Otherwise the trim char would cover just half of it.
- if width := cvs.Area().Dx(); width > 1 {
- penUlt := image.Point{width - 2, line}
- prev, err := cvs.Cell(penUlt)
- if err != nil {
- return err
- }
-
- if runewidth.RuneWidth(prev.Rune) == 2 {
- if _, err := cvs.SetCell(penUlt, 0); err != nil {
- return err
- }
- }
- }
-
- cells, err := cvs.SetCell(lastPoint, '…')
- if err != nil {
- return err
- }
- if cells != 1 {
- panic(fmt.Errorf("invalid trim character, it occupies %d cells, the implementation only supports scroll markers that occupy exactly one cell", cells))
- }
- return nil
-}
-
-// lineTrim determines if the current line needs to be trimmed. The cvs is the
-// canvas assigned to the widget, the curPoint is the current point the widget
-// is going to place the curRune at. If line trimming is needed, this function
-// replaces the last character with the horizontal ellipsis '…' character.
-func lineTrim(cvs *canvas.Canvas, curPoint image.Point, curRune rune, opts *options) (*trimResult, error) {
- if opts.wrapMode == wrap.AtRunes {
- // Don't trim if the widget is configured to wrap lines.
- return &trimResult{
- trimmed: false,
- curPoint: curPoint,
- }, nil
- }
-
- // Newline characters are never trimmed, they start the next line.
- if curRune == '\n' {
- return &trimResult{
- trimmed: false,
- curPoint: curPoint,
- }, nil
- }
-
- width := cvs.Area().Dx()
- rw := runewidth.RuneWidth(curRune)
- switch {
- case rw == 1:
- if curPoint.X == width {
- if err := drawTrimChar(cvs, curPoint.Y); err != nil {
- return nil, err
- }
- }
-
- case rw == 2:
- if curPoint.X == width || curPoint.X == width-1 {
- if err := drawTrimChar(cvs, curPoint.Y); err != nil {
- return nil, err
- }
- }
-
- default:
- return nil, fmt.Errorf("unable to decide line trimming at position %v for rune %q which has an unsupported width %d", curPoint, curRune, rw)
- }
-
- trimmed := curPoint.X > width-rw
- if trimmed {
- curPoint = image.Point{curPoint.X + rw, curPoint.Y}
- }
- return &trimResult{
- trimmed: trimmed,
- curPoint: curPoint,
- }, nil
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/options.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/options.go
deleted file mode 100644
index b91cec85e..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/options.go
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package text
-
-import (
- "fmt"
-
- "github.com/mum4k/termdash/keyboard"
- "github.com/mum4k/termdash/mouse"
- "github.com/mum4k/termdash/private/wrap"
-)
-
-// options.go contains configurable options for Text.
-
-// Option is used to provide options to New().
-type Option interface {
- // set sets the provided option.
- set(*options)
-}
-
-// options stores the provided options.
-type options struct {
- wrapMode wrap.Mode
- rollContent bool
- disableScrolling bool
- mouseUpButton mouse.Button
- mouseDownButton mouse.Button
- keyUp keyboard.Key
- keyDown keyboard.Key
- keyPgUp keyboard.Key
- keyPgDown keyboard.Key
-}
-
-// newOptions returns a new options instance.
-func newOptions(opts ...Option) *options {
- opt := &options{
- mouseUpButton: DefaultScrollMouseButtonUp,
- mouseDownButton: DefaultScrollMouseButtonDown,
- keyUp: DefaultScrollKeyUp,
- keyDown: DefaultScrollKeyDown,
- keyPgUp: DefaultScrollKeyPageUp,
- keyPgDown: DefaultScrollKeyPageDown,
- }
- for _, o := range opts {
- o.set(opt)
- }
- return opt
-}
-
-// validate validates the provided options.
-func (o *options) validate() error {
- keys := map[keyboard.Key]bool{
- o.keyUp: true,
- o.keyDown: true,
- o.keyPgUp: true,
- o.keyPgDown: true,
- }
- if len(keys) != 4 {
- return fmt.Errorf("invalid ScrollKeys(up:%v, down:%v, pageUp:%v, pageDown:%v), the keys must be unique", o.keyUp, o.keyDown, o.keyPgUp, o.keyPgDown)
- }
- if o.mouseUpButton == o.mouseDownButton {
- return fmt.Errorf("invalid ScrollMouseButtons(up:%v, down:%v), the buttons must be unique", o.mouseUpButton, o.mouseDownButton)
- }
- return nil
-}
-
-// option implements Option.
-type option func(*options)
-
-// set implements Option.set.
-func (o option) set(opts *options) {
- o(opts)
-}
-
-// WrapAtWords configures the text widget so that it automatically wraps lines
-// that are longer than the width of the widget at word boundaries. If not
-// provided, long lines are trimmed instead.
-func WrapAtWords() Option {
- return option(func(opts *options) {
- opts.wrapMode = wrap.AtWords
- })
-}
-
-// WrapAtRunes configures the text widget so that it automatically wraps lines
-// that are longer than the width of the widget at rune boundaries. If not
-// provided, long lines are trimmed instead.
-func WrapAtRunes() Option {
- return option(func(opts *options) {
- opts.wrapMode = wrap.AtRunes
- })
-}
-
-// RollContent configures the text widget so that it rolls the text content up
-// if more text than the size of the container is added. If not provided, the
-// content is trimmed instead.
-func RollContent() Option {
- return option(func(opts *options) {
- opts.rollContent = true
- })
-}
-
-// DisableScrolling disables the scrolling of the content using keyboard and
-// mouse.
-func DisableScrolling() Option {
- return option(func(opts *options) {
- opts.disableScrolling = true
- })
-}
-
-// The default mouse buttons for content scrolling.
-const (
- DefaultScrollMouseButtonUp = mouse.ButtonWheelUp
- DefaultScrollMouseButtonDown = mouse.ButtonWheelDown
-)
-
-// ScrollMouseButtons configures the mouse buttons that scroll the content.
-// The provided buttons must be unique, e.g. the same button cannot be both up
-// and down.
-func ScrollMouseButtons(up, down mouse.Button) Option {
- return option(func(opts *options) {
- opts.mouseUpButton = up
- opts.mouseDownButton = down
- })
-}
-
-// The default keys for content scrolling.
-const (
- DefaultScrollKeyUp = keyboard.KeyArrowUp
- DefaultScrollKeyDown = keyboard.KeyArrowDown
- DefaultScrollKeyPageUp = keyboard.KeyPgUp
- DefaultScrollKeyPageDown = keyboard.KeyPgDn
-)
-
-// ScrollKeys configures the keyboard keys that scroll the content.
-// The provided keys must be unique, e.g. the same key cannot be both up and
-// down.
-func ScrollKeys(up, down, pageUp, pageDown keyboard.Key) Option {
- return option(func(opts *options) {
- opts.keyUp = up
- opts.keyDown = down
- opts.keyPgUp = pageUp
- opts.keyPgDown = pageDown
- })
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/scroll.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/scroll.go
deleted file mode 100644
index f63092fdb..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/scroll.go
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package text
-
-// scroll.go contains code that tracks the current scrolling position.
-
-import "math"
-
-// scrollTracker tracks the current scrolling position for the Text widget.
-//
-// The text widget displays the contained text buffer as lines of text that fit
-// the widget's canvas. The main goal of this object is to inform the text
-// widget which should be the first drawn line from the buffer. This depends on
-// two things, the scrolling position based on user inputs and whether the text
-// widget is configured to roll the content up as new text is added by the
-// client.
-//
-// The rolling Vs. scrolling state is tracked in an FSM implemented in this
-// file.
-//
-// The client can scroll the content by either a keyboard or a mouse event. The
-// widget receives these events concurrently with requests to redraw the
-// content, so this objects keeps a track of all the scrolling events that
-// happened since the last redraw and consumes them when calculating which is
-// the first drawn line on the next redraw event.
-//
-// This is not thread safe.
-type scrollTracker struct {
- // scroll stores user requests to scroll up (negative) or down (positive).
- // E.g. -1 means up by one line and 2 means down by two lines.
- scroll int
-
- // scrollPage stores user requests to scroll up (negative) or down
- // (positive) by a page of content. E.g. -1 means up by one page and 2
- // means down by two pages.
- scrollPage int
-
- // first tracks the first line that will be printed.
- first int
-
- // state is the state of the scrolling FSM.
- state rollState
-}
-
-// newScrollTracker returns a new scroll tracker.
-func newScrollTracker(opts *options) *scrollTracker {
- if opts.rollContent {
- return &scrollTracker{state: rollToEnd}
- }
- return &scrollTracker{state: rollingDisabled}
-}
-
-// upOneLine processes a user request to scroll up by one line.
-func (st *scrollTracker) upOneLine() {
- st.scroll--
-}
-
-// downOneLine processes a user request to scroll down by one line.
-func (st *scrollTracker) downOneLine() {
- st.scroll++
-}
-
-// upOnePage processes a user request to scroll up by one page.
-func (st *scrollTracker) upOnePage() {
- st.scrollPage--
-}
-
-// downOnePage processes a user request to scroll down by one page.
-func (st *scrollTracker) downOnePage() {
- st.scrollPage++
-}
-
-// doScroll processes any outstanding scroll requests and calculates the
-// resulting first line.
-func (st *scrollTracker) doScroll(lines, height int) int {
- first := st.first + st.scroll + st.scrollPage*height
- st.scroll = 0
- st.scrollPage = 0
- return normalizeScroll(first, lines, height)
-}
-
-// firstLine returns the number of the first line that should be drawn on a
-// canvas of the specified height if there is the provided number of lines of
-// text.
-func (st *scrollTracker) firstLine(lines, height int) int {
- // Execute the scrolling FSM.
- st.state = st.state(st, lines, height)
- return st.first
-}
-
-// rollState is a state in the scrolling FSM.
-type rollState func(st *scrollTracker, lines, height int) rollState
-
-// rollingDisabled is a state where content rolling was disabled by the
-// configuration of the Text widget.
-func rollingDisabled(st *scrollTracker, lines, height int) rollState {
- st.first = st.doScroll(lines, height)
- return rollingDisabled
-}
-
-// rollToEnd is a state in which the last line of the content is always
-// visible. When new content arrives, it is rolled upwards.
-func rollToEnd(st *scrollTracker, lines, height int) rollState {
- // If the user didn't scroll, just roll the content so that the last line
- // is visible.
- if st.scroll == 0 && st.scrollPage == 0 {
- st.first = normalizeScroll(math.MaxInt32, lines, height)
- return rollToEnd
- }
-
- st.first = st.doScroll(lines, height)
- if lastLineVisible(st.first, lines, height) {
- return rollToEnd
- }
- return rollingPaused
-}
-
-// rollingPaused is a state in which the user scrolled up and made the last
-// line scroll out of the view, so the content rolling is paused.
-func rollingPaused(st *scrollTracker, lines, height int) rollState {
- st.first = st.doScroll(lines, height)
- if lastLineVisible(st.first, lines, height) {
- return rollToEnd
- }
- return rollingPaused
-}
-
-// lastLineVisible returns true if the last text line from within the buffer of
-// the text widget is visible on the canvas when drawing of the text starts
-// from the specified start line, there is the provided total amount of lines
-// and the canvas has the height.
-func lastLineVisible(start, lines, height int) bool {
- return lines-start <= height
-}
-
-// normalizeScroll returns normalized position of the first line that should be
-// drawn when drawing the specified number of lines on a canvas with the
-// provided height.
-func normalizeScroll(first, lines, height int) int {
- if first < 0 || lines <= 0 || height <= 0 {
- return 0
- }
-
- if lines <= height {
- return 0 // Scrolling not necessary if the content fits.
- }
-
- max := lines - height
- if first > max {
- return max
- }
- return first
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/text.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/text.go
deleted file mode 100644
index 0712cbcbe..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/text.go
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Package text contains a widget that displays textual data.
-package text
-
-import (
- "fmt"
- "image"
- "sync"
-
- "github.com/mum4k/termdash/private/canvas"
- "github.com/mum4k/termdash/private/canvas/buffer"
- "github.com/mum4k/termdash/private/wrap"
- "github.com/mum4k/termdash/terminal/terminalapi"
- "github.com/mum4k/termdash/widgetapi"
-)
-
-// Text displays a block of text.
-//
-// Each line of the text is either trimmed or wrapped according to the provided
-// options. The entire text content is either trimmed or rolled up through the
-// canvas according to the provided options.
-//
-// By default the widget supports scrolling of content with either the keyboard
-// or mouse. See the options for the default keys and mouse buttons.
-//
-// Implements widgetapi.Widget. This object is thread-safe.
-type Text struct {
- // content is the text content that will be displayed in the widget as
- // provided by the caller (i.e. not wrapped or pre-processed).
- content []*buffer.Cell
- // wrapped is the content wrapped to the current width of the canvas.
- wrapped [][]*buffer.Cell
-
- // scroll tracks scrolling the position.
- scroll *scrollTracker
-
- // lastWidth stores the width of the last canvas the widget drew on.
- // Used to determine if the previous line wrapping was invalidated.
- lastWidth int
- // contentChanged indicates if the text content of the widget changed since
- // the last drawing. Used to determine if the previous line wrapping was
- // invalidated.
- contentChanged bool
-
- // mu protects the Text widget.
- mu sync.Mutex
-
- // opts are the provided options.
- opts *options
-}
-
-// New returns a new text widget.
-func New(opts ...Option) (*Text, error) {
- opt := newOptions(opts...)
- if err := opt.validate(); err != nil {
- return nil, err
- }
- return &Text{
- scroll: newScrollTracker(opt),
- opts: opt,
- }, nil
-}
-
-// Reset resets the widget back to empty content.
-func (t *Text) Reset() {
- t.mu.Lock()
- defer t.mu.Unlock()
- t.reset()
-}
-
-// reset implements Reset, caller must hold t.mu.
-func (t *Text) reset() {
- t.content = nil
- t.wrapped = nil
- t.scroll = newScrollTracker(t.opts)
- t.lastWidth = 0
- t.contentChanged = true
-}
-
-// Write writes text for the widget to display. Multiple calls append
-// additional text. The text contain cannot control characters
-// (unicode.IsControl) or space character (unicode.IsSpace) other than:
-// ' ', '\n'
-// Any newline ('\n') characters are interpreted as newlines when displaying
-// the text.
-func (t *Text) Write(text string, wOpts ...WriteOption) error {
- t.mu.Lock()
- defer t.mu.Unlock()
-
- if err := wrap.ValidText(text); err != nil {
- return err
- }
-
- opts := newWriteOptions(wOpts...)
- if opts.replace {
- t.reset()
- }
- for _, r := range text {
- t.content = append(t.content, buffer.NewCell(r, opts.cellOpts))
- }
- t.contentChanged = true
- return nil
-}
-
-// minLinesForMarkers are the minimum amount of lines required on the canvas in
-// order to draw the scroll markers ('⇧' and '⇩').
-const minLinesForMarkers = 3
-
-// drawScrollUp draws the scroll up marker on the first line if there is more
-// text "above" the canvas due to the scrolling position. Returns true if the
-// marker was drawn.
-func (t *Text) drawScrollUp(cvs *canvas.Canvas, cur image.Point, fromLine int) (bool, error) {
- height := cvs.Area().Dy()
- if cur.Y == 0 && height >= minLinesForMarkers && fromLine > 0 {
- cells, err := cvs.SetCell(cur, '⇧')
- if err != nil {
- return false, err
- }
- if cells != 1 {
- panic(fmt.Errorf("invalid scroll up marker, it occupies %d cells, the implementation only supports scroll markers that occupy exactly one cell", cells))
- }
- return true, nil
- }
- return false, nil
-}
-
-// drawScrollDown draws the scroll down marker on the last line if there is
-// more text "below" the canvas due to the scrolling position. Returns true if
-// the marker was drawn.
-func (t *Text) drawScrollDown(cvs *canvas.Canvas, cur image.Point, fromLine int) (bool, error) {
- height := cvs.Area().Dy()
- lines := len(t.wrapped)
- if cur.Y == height-1 && height >= minLinesForMarkers && height < lines-fromLine {
- cells, err := cvs.SetCell(cur, '⇩')
- if err != nil {
- return false, err
- }
- if cells != 1 {
- panic(fmt.Errorf("invalid scroll down marker, it occupies %d cells, the implementation only supports scroll markers that occupy exactly one cell", cells))
- }
- return true, nil
- }
- return false, nil
-}
-
-// draw draws the text context on the canvas starting at the specified line.
-func (t *Text) draw(cvs *canvas.Canvas) error {
- var cur image.Point // Tracks the current drawing position on the canvas.
- height := cvs.Area().Dy()
- fromLine := t.scroll.firstLine(len(t.wrapped), height)
-
- for _, line := range t.wrapped[fromLine:] {
- // Scroll up marker.
- scrlUp, err := t.drawScrollUp(cvs, cur, fromLine)
- if err != nil {
- return err
- }
- if scrlUp {
- cur = image.Point{0, cur.Y + 1} // Move to the next line.
- // Skip one line of text, the marker replaced it.
- continue
- }
-
- // Scroll down marker.
- scrlDown, err := t.drawScrollDown(cvs, cur, fromLine)
- if err != nil {
- return err
- }
- if scrlDown || cur.Y >= height {
- break // Skip all lines falling after (under) the canvas.
- }
-
- for _, cell := range line {
- tr, err := lineTrim(cvs, cur, cell.Rune, t.opts)
- if err != nil {
- return err
- }
- cur = tr.curPoint
- if tr.trimmed {
- break // Skip over any characters trimmed on the current line.
- }
-
- cells, err := cvs.SetCell(cur, cell.Rune, cell.Opts)
- if err != nil {
- return err
- }
- cur = image.Point{cur.X + cells, cur.Y} // Move within the same line.
- }
- cur = image.Point{0, cur.Y + 1} // Move to the next line.
- }
- return nil
-}
-
-// Draw draws the text onto the canvas.
-// Implements widgetapi.Widget.Draw.
-func (t *Text) Draw(cvs *canvas.Canvas, meta *widgetapi.Meta) error {
- t.mu.Lock()
- defer t.mu.Unlock()
-
- width := cvs.Area().Dx()
- if len(t.content) > 0 && (t.contentChanged || t.lastWidth != width) {
- // The previous text preprocessing (line wrapping) is invalidated when
- // new text is added or the width of the canvas changed.
- wr, err := wrap.Cells(t.content, width, t.opts.wrapMode)
- if err != nil {
- return err
- }
- t.wrapped = wr
- }
- t.lastWidth = width
-
- if len(t.wrapped) == 0 {
- return nil // Nothing to draw if there's no text.
- }
-
- if err := t.draw(cvs); err != nil {
- return err
- }
- t.contentChanged = false
- return nil
-}
-
-// Keyboard implements widgetapi.Widget.Keyboard.
-func (t *Text) Keyboard(k *terminalapi.Keyboard) error {
- t.mu.Lock()
- defer t.mu.Unlock()
-
- switch {
- case k.Key == t.opts.keyUp:
- t.scroll.upOneLine()
- case k.Key == t.opts.keyDown:
- t.scroll.downOneLine()
- case k.Key == t.opts.keyPgUp:
- t.scroll.upOnePage()
- case k.Key == t.opts.keyPgDown:
- t.scroll.downOnePage()
- }
- return nil
-}
-
-// Mouse implements widgetapi.Widget.Mouse.
-func (t *Text) Mouse(m *terminalapi.Mouse) error {
- t.mu.Lock()
- defer t.mu.Unlock()
-
- switch b := m.Button; {
- case b == t.opts.mouseUpButton:
- t.scroll.upOneLine()
- case b == t.opts.mouseDownButton:
- t.scroll.downOneLine()
- }
- return nil
-}
-
-// Options of the widget
-func (t *Text) Options() widgetapi.Options {
- var ks widgetapi.KeyScope
- var ms widgetapi.MouseScope
- if t.opts.disableScrolling {
- ks = widgetapi.KeyScopeNone
- ms = widgetapi.MouseScopeNone
- } else {
- ks = widgetapi.KeyScopeFocused
- ms = widgetapi.MouseScopeWidget
- }
-
- return widgetapi.Options{
- // At least one line with at least one full-width rune.
- MinimumSize: image.Point{1, 1},
- WantMouse: ms,
- WantKeyboard: ks,
- }
-}
diff --git a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/write_options.go b/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/write_options.go
deleted file mode 100644
index ddb5c40bc..000000000
--- a/examples/go-dashboard/src/github.com/mum4k/termdash/widgets/text/write_options.go
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2018 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package text
-
-// write_options.go contains options used when writing content to the Text widget.
-
-import (
- "github.com/mum4k/termdash/cell"
-)
-
-// WriteOption is used to provide options to Write().
-type WriteOption interface {
- // set sets the provided option.
- set(*writeOptions)
-}
-
-// writeOptions stores the provided options.
-type writeOptions struct {
- cellOpts *cell.Options
- replace bool
-}
-
-// newWriteOptions returns new writeOptions instance.
-func newWriteOptions(wOpts ...WriteOption) *writeOptions {
- wo := &writeOptions{
- cellOpts: cell.NewOptions(),
- }
- for _, o := range wOpts {
- o.set(wo)
- }
- return wo
-}
-
-// writeOption implements WriteOption.
-type writeOption func(*writeOptions)
-
-// set implements WriteOption.set.
-func (wo writeOption) set(wOpts *writeOptions) {
- wo(wOpts)
-}
-
-// WriteCellOpts sets options on the cells that contain the text.
-func WriteCellOpts(opts ...cell.Option) WriteOption {
- return writeOption(func(wOpts *writeOptions) {
- wOpts.cellOpts = cell.NewOptions(opts...)
- })
-}
-
-// WriteReplace instructs the text widget to replace the entire text content on
-// this write instead of appending.
-func WriteReplace() WriteOption {
- return writeOption(func(wOpts *writeOptions) {
- wOpts.replace = true
- })
-}