Compare commits

...

171 Commits
v0.2.0 ... main

Author SHA1 Message Date
Navid Yaghoobi
7b0dba28a6
Merge pull request #81 from navidys/dependabot/go_modules/github.com/gdamore/tcell/v2-2.8.1
Bump github.com/gdamore/tcell/v2 from 2.7.4 to 2.8.1
2025-01-13 21:46:37 +11:00
dependabot[bot]
f7a965b9b1
Bump github.com/gdamore/tcell/v2 from 2.7.4 to 2.8.1
Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.7.4 to 2.8.1.
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv2.md)
- [Commits](https://github.com/gdamore/tcell/compare/v2.7.4...v2.8.1)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-13 10:29:46 +00:00
Navid Yaghoobi
7a7632ee73
Merge pull request #79 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.22.2
Bump github.com/onsi/ginkgo/v2 from 2.22.1 to 2.22.2
2025-01-02 10:35:51 +11:00
dependabot[bot]
5c75618ec1
Bump github.com/onsi/ginkgo/v2 from 2.22.1 to 2.22.2
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.22.1 to 2.22.2.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.22.1...v2.22.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-31 10:16:57 +00:00
Navid Yaghoobi
657ca691c4
Merge pull request #77 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.22.1
Bump github.com/onsi/ginkgo/v2 from 2.21.0 to 2.22.1
2024-12-22 22:08:53 +11:00
dependabot[bot]
5c8253e8d9
Bump github.com/onsi/ginkgo/v2 from 2.21.0 to 2.22.1
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.21.0 to 2.22.1.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.21.0...v2.22.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-20 10:33:37 +00:00
Navid Yaghoobi
487c87b304
Merge pull request #71 from navidys/dependabot/go_modules/github.com/onsi/gomega-1.35.0
Bump github.com/onsi/gomega from 1.34.2 to 1.35.0
2024-10-30 21:20:27 +11:00
dependabot[bot]
d80a351c3c
Bump github.com/onsi/gomega from 1.34.2 to 1.35.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.34.2 to 1.35.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.34.2...v1.35.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-30 10:17:32 +00:00
Navid Yaghoobi
9dc06ec28d
Merge pull request #72 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.21.0
Bump github.com/onsi/ginkgo/v2 from 2.20.2 to 2.21.0
2024-10-30 21:16:20 +11:00
dependabot[bot]
04fd610c5b
Bump github.com/onsi/ginkgo/v2 from 2.20.2 to 2.21.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.20.2 to 2.21.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.20.2...v2.21.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-30 10:12:38 +00:00
Navid Yaghoobi
8a454d1c5e fix gh action failure
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-10-26 21:29:02 +11:00
Navid Yaghoobi
b6def86254
Merge pull request #70 from navidys/golangci_lint_update
golangci-lint update 1.61.0
2024-10-26 21:19:04 +11:00
Navid Yaghoobi
f97e54d849 golangci-lint update 1.61.0
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-10-26 21:16:51 +11:00
Navid Yaghoobi
3ddb90bd10
Merge pull request #69 from markusressel/feature/custom-x-axis-labeling
Feature: Allow custom X-Axis labeling
2024-10-13 22:29:42 +11:00
Navid Yaghoobi
091fcae5c5 running golangci-lint
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-10-13 22:28:43 +11:00
Markus Ressel
12e9f953a3
lint fixes
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 12:22:19 +02:00
Markus Ressel
fb7e4a8141
run gofmt -s -w
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 12:19:54 +02:00
Markus Ressel
362ed76a2b
lint fix
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 12:17:32 +02:00
Markus Ressel
a6b632c4f6
lint fix
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:48:58 +02:00
Markus Ressel
7e165355fc
cleanup demo
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:41:10 +02:00
Markus Ressel
00c6cca493
update docs, fix small error in demo
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:40:11 +02:00
Markus Ressel
543712cedb
integrate custom y range feature
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:24:54 +02:00
Markus Ressel
329fcead2e
refactor
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:14 +02:00
Markus Ressel
ca09c00451
refactor
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
2036aacbcb
simplify code
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
253e22ef1a
performance improvement
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
ac5e17721f
fix typo
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
d3a7c777e0
fix slice rotation in main demo
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
63d5ed1494
reset animation after multiple periods
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
0d4a60f69d
toggle animation on click
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
ed9012b1bf
refactor
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
5d0440879d
improve example
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
bb6ca0a79c
cleanup
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
bba8fe74ca
refactor
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
6119df590d
simplify
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
004135d96f
add screenshot
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
cb36b86f14
rework drawXAxisLabelToScreen to support drawing custom x-axis labels, add custom demo
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
d3cf38290d
do not write labels beyond bounds
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
d46f48fbe0
do not write labels beyond bounds
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Markus Ressel
d3dc728102
added SetXAxisLabelFunc method to provide custom X-Axis labels
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-13 10:23:06 +02:00
Navid Yaghoobi
40e5944bdc
Merge pull request #68 from markusressel/feature/allow-y-axis-range-override
allow manual override of Y-Axis MinValue and MaxValue
2024-10-13 12:48:55 +11:00
Navid Yaghoobi
3cfbbb446a running golangci-lint
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-10-13 12:44:01 +11:00
Markus Ressel
718914020b
change call order to better reflect real world usage
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 17:46:57 +02:00
Markus Ressel
a89f40eb4c
fix demo, add SetYRange to set both min and max at the same time
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 17:35:45 +02:00
Markus Ressel
d550a262e6
refactor and simplify calcBrailleLines logic and respect bounds
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 17:04:38 +02:00
Markus Ressel
e468a23392
fix property visibility
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 15:03:43 +02:00
Markus Ressel
83dbdb49e6
add screenshot
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 14:55:41 +02:00
Markus Ressel
7e3024f613
add parameters to control auto scaling behavior and prevent breaking change
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 14:54:53 +02:00
Markus Ressel
4632165f80
do not draw points that exceed the available height, fix scatter plots
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 14:45:42 +02:00
Markus Ressel
9d6693060a
revert changes to original plot example, add custom_range example
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 14:19:37 +02:00
Markus Ressel
eafbe24268
apply NaN fix to getMinFloat64From2dSlice
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 03:46:05 +02:00
Markus Ressel
e95a6c091d
allow manual override of Y-Axis MinValue and MaxValue
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-12 03:44:35 +02:00
Navid Yaghoobi
89b22f5d1d
Merge pull request #67 from markusressel/feature/do-not-draw-math-nan-values
ignore math.NaN() values when drawing plots
2024-10-12 12:03:06 +11:00
Navid Yaghoobi
5f3f2f99fc running golangci-lint
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-10-12 12:01:56 +11:00
Markus Ressel
6ec082d4f7
draw single valid data points in braille plot
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-11 22:42:46 +02:00
Markus Ressel
444dcd17a9
fix wrong braille line when data contains math.NaN
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-11 22:29:41 +02:00
Markus Ressel
170954e0d7
ignore math.NaN() values when drawing sparkline plots
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-11 01:19:47 +02:00
Markus Ressel
01b00ebe51
ignore math.NaN() values when determining max value
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-11 01:01:31 +02:00
Markus Ressel
3dd7ecf34b ignore math.NaN() values when drawing plots
Signed-off-by: Markus Ressel <mail@markusressel.de>
2024-10-11 00:32:08 +02:00
Navid Yaghoobi
ddfd01579e
Merge pull request #66 from navidys/plot_y_axis_type
add plot Y axis label type (float, integer)
2024-09-28 12:34:53 +10:00
Navid Yaghoobi
1dfc3feec1 add plot Y axis label type (float, integer)
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-09-28 12:33:20 +10:00
Navid Yaghoobi
ef7a0912d9
Merge pull request #65 from navidys/lint
running golangci-lint
2024-09-28 12:17:54 +10:00
Navid Yaghoobi
4aedeab3b0 running golangci-lint
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-09-28 12:16:31 +10:00
Navid Yaghoobi
1110c53694
Merge pull request #59 from Topvennie/bars
Delete bar items
2024-09-28 12:09:41 +10:00
Navid Yaghoobi
9cfa279d8d
Merge pull request #63 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.20.2
Bump github.com/onsi/ginkgo/v2 from 2.20.0 to 2.20.2
2024-09-27 20:12:10 +10:00
dependabot[bot]
5625bd86d5
Bump github.com/onsi/ginkgo/v2 from 2.20.0 to 2.20.2
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.20.0 to 2.20.2.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.20.0...v2.20.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-27 10:03:02 +00:00
Navid Yaghoobi
43529b16f2
Merge pull request #64 from navidys/dependabot/go_modules/github.com/onsi/gomega-1.34.2
Bump github.com/onsi/gomega from 1.34.1 to 1.34.2
2024-09-27 20:01:53 +10:00
dependabot[bot]
c35dc08b42
Bump github.com/onsi/gomega from 1.34.1 to 1.34.2
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.34.1 to 1.34.2.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.34.1...v1.34.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-27 09:37:50 +00:00
Navid Yaghoobi
984a311125 update go to 1.22.6
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-09-27 19:36:33 +10:00
Topvennie
335e9835f3
chore(bar): linting 2024-08-21 14:05:33 +02:00
Topvennie
a65ef0c1c5
chore(bar): allow multiple bars to be deleted at once 2024-08-14 23:19:41 +02:00
Topvennie
224c39bcd2
feat(bar): remove bars 2024-08-14 23:14:06 +02:00
Navid Yaghoobi
3a2e991738
Merge pull request #57 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.20.0
Bump github.com/onsi/ginkgo/v2 from 2.19.1 to 2.20.0
2024-08-12 08:07:02 +10:00
dependabot[bot]
1946bda711
Bump github.com/onsi/ginkgo/v2 from 2.19.1 to 2.20.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.19.1 to 2.20.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.19.1...v2.20.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 11:02:34 +00:00
Navid Yaghoobi
f1922269c0
Merge pull request #54 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.19.1
Bump github.com/onsi/ginkgo/v2 from 2.19.0 to 2.19.1
2024-07-30 21:30:56 +10:00
dependabot[bot]
aa9f7893de
Bump github.com/onsi/ginkgo/v2 from 2.19.0 to 2.19.1
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.19.0 to 2.19.1.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.19.0...v2.19.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 10:23:49 +00:00
Navid Yaghoobi
d5dda69eaa remove cirrus
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-07-28 20:31:41 +10:00
Navid Yaghoobi
770f88c853
Merge pull request #53 from navidys/go_version
go version 1.21.0
2024-07-28 20:28:57 +10:00
Navid Yaghoobi
8987c247bb go version 1.21.0
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-07-28 20:25:17 +10:00
Navid Yaghoobi
7ee00ea004 go version 1.21.0
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-07-28 20:22:24 +10:00
Navid Yaghoobi
903d9edb12
Merge pull request #52 from navidys/dialog_title_bugfix
bugfix for dialog title display
2024-06-21 18:14:02 +10:00
Navid Yaghoobi
09f153c313 bugfix for dialog title display
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-06-21 18:09:37 +10:00
Navid Yaghoobi
c5e08f5545
Merge pull request #51 from navidys/tview_vendor_update
tview module update
2024-06-21 18:03:59 +10:00
Navid Yaghoobi
383bde043c tview module update
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-06-21 18:01:38 +10:00
Navid Yaghoobi
b555c093da
Merge pull request #49 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.19.0
Bump github.com/onsi/ginkgo/v2 from 2.18.0 to 2.19.0
2024-06-08 18:54:19 +10:00
dependabot[bot]
e3a8e9ee6f
Bump github.com/onsi/ginkgo/v2 from 2.18.0 to 2.19.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.18.0 to 2.19.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.18.0...v2.19.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-08 08:34:36 +00:00
Navid Yaghoobi
24c34d8655 github action update
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-06-08 18:33:44 +10:00
Navid Yaghoobi
578159ac6f running codespell
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-06-08 18:29:19 +10:00
Navid Yaghoobi
458cbc6c7c
Merge pull request #48 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.18.0
Bump github.com/onsi/ginkgo/v2 from 2.17.3 to 2.18.0
2024-05-23 06:37:50 +10:00
dependabot[bot]
0cac81fb0c
---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-22 10:41:51 +00:00
Navid Yaghoobi
f98902679a
Merge pull request #40 from navidys/dependabot/go_modules/github.com/onsi/gomega-1.33.1
Bump github.com/onsi/gomega from 1.32.0 to 1.33.1
2024-05-11 19:15:55 +10:00
dependabot[bot]
f797d8124c
Bump github.com/onsi/gomega from 1.32.0 to 1.33.1
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.32.0 to 1.33.1.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.32.0...v1.33.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-11 09:14:45 +00:00
Navid Yaghoobi
e4b840b0d7
Merge pull request #45 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.17.3
Bump github.com/onsi/ginkgo/v2 from 2.17.1 to 2.17.3
2024-05-11 19:14:03 +10:00
dependabot[bot]
87b5f8820b
Bump github.com/onsi/ginkgo/v2 from 2.17.1 to 2.17.3
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.17.1 to 2.17.3.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.17.1...v2.17.3)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-05-11 09:11:10 +00:00
Navid Yaghoobi
78b9f38c5a
Merge pull request #47 from navidys/ghaction
fix codecoverage upload action
2024-05-11 19:10:02 +10:00
Navid Yaghoobi
29c7875502 fix codecoverage upload action
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-05-11 19:08:08 +10:00
Navid Yaghoobi
03accf7634
Merge pull request #44 from anderspitman/expose-plot-rect
Expose GetPlotRect in public interface
2024-05-08 06:19:13 +10:00
Anders Pitman
e9eb5cdbdc
Expose GetPlotRect 2024-05-07 12:23:28 -06:00
Navid Yaghoobi
f4a44f8b74
Merge pull request #37 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.17.1
Bump github.com/onsi/ginkgo/v2 from 2.17.0 to 2.17.1
2024-03-26 09:32:11 +11:00
dependabot[bot]
bb2e442902
Bump github.com/onsi/ginkgo/v2 from 2.17.0 to 2.17.1
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.17.0 to 2.17.1.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.17.0...v2.17.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 10:49:20 +00:00
Navid Yaghoobi
c1149f4730
Merge pull request #35 from navidys/dependabot/go_modules/github.com/onsi/gomega-1.32.0
Bump github.com/onsi/gomega from 1.31.1 to 1.32.0
2024-03-20 15:11:24 +11:00
dependabot[bot]
9f6c73d584
Bump github.com/onsi/gomega from 1.31.1 to 1.32.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.31.1 to 1.32.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.31.1...v1.32.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-20 04:10:00 +00:00
Navid Yaghoobi
9a8419ba8b
Merge pull request #36 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.17.0
Bump github.com/onsi/ginkgo/v2 from 2.16.0 to 2.17.0
2024-03-20 15:09:23 +11:00
dependabot[bot]
852d3a1c2b
Bump github.com/onsi/ginkgo/v2 from 2.16.0 to 2.17.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.16.0 to 2.17.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.16.0...v2.17.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-19 11:02:08 +00:00
Navid Yaghoobi
8c8addff08
Merge pull request #33 from navidys/dependabot/go_modules/github.com/onsi/ginkgo/v2-2.16.0
Bump github.com/onsi/ginkgo/v2 from 2.15.0 to 2.16.0
2024-03-05 22:06:31 +11:00
dependabot[bot]
2ef68bb07e
Bump github.com/onsi/ginkgo/v2 from 2.15.0 to 2.16.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.15.0 to 2.16.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.15.0...v2.16.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-05 11:02:33 +00:00
Navid Yaghoobi
f0f35dace5 golangci-lint update 1.56.2
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-03-05 22:01:34 +11:00
Navid Yaghoobi
f50a0fb83b
Merge pull request #34 from navidys/golangci_lint
golangci-lint update 1.56.2
2024-03-05 21:58:05 +11:00
Navid Yaghoobi
36e3d86da4 golangci-lint update 1.56.2
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-03-05 21:56:13 +11:00
Navid Yaghoobi
89ca96e1d6
Merge pull request #32 from navidys/dependabot/go_modules/github.com/gdamore/tcell/v2-2.7.4
Bump github.com/gdamore/tcell/v2 from 2.7.1 to 2.7.4
2024-03-04 22:09:16 +11:00
dependabot[bot]
113ea8b938
Bump github.com/gdamore/tcell/v2 from 2.7.1 to 2.7.4
Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.7.1 to 2.7.4.
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv2.md)
- [Commits](https://github.com/gdamore/tcell/compare/v2.7.1...v2.7.4)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 10:50:19 +00:00
Navid Yaghoobi
d640b32096
Merge pull request #31 from navidys/dependabot/go_modules/github.com/gdamore/tcell/v2-2.7.1
Bump github.com/gdamore/tcell/v2 from 2.7.0 to 2.7.1
2024-02-19 21:37:11 +11:00
dependabot[bot]
1c80e02217
Bump github.com/gdamore/tcell/v2 from 2.7.0 to 2.7.1
Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.7.0 to 2.7.1.
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv2.md)
- [Commits](https://github.com/gdamore/tcell/compare/v2.7.0...v2.7.1)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 10:10:01 +00:00
Navid Yaghoobi
55176a3e08
Merge pull request #30 from navidys/go_version
go version update to 1.20
2024-01-27 20:51:04 +11:00
Navid Yaghoobi
fe7c2ef80c go version update to 1.20
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2024-01-27 20:48:23 +11:00
Navid Yaghoobi
172e1e9661
Merge pull request #29 from navidys/dependabot/go_modules/github.com/onsi/gomega-1.31.1
Bump github.com/onsi/gomega from 1.31.0 to 1.31.1
2024-01-22 21:55:39 +11:00
dependabot[bot]
6ef8527c29
Bump github.com/onsi/gomega from 1.31.0 to 1.31.1
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.31.0 to 1.31.1.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.31.0...v1.31.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-22 10:11:11 +00:00
Navid Yaghoobi
20d0b5b64b
Merge pull request #28 from navidys/dependabot/go_modules/github.com/onsi/gomega-1.31.0
Bump github.com/onsi/gomega from 1.30.0 to 1.31.0
2024-01-19 09:09:28 +11:00
dependabot[bot]
0d11f83a24
Bump github.com/onsi/gomega from 1.30.0 to 1.31.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.30.0 to 1.31.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.30.0...v1.31.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-18 22:04:01 +00:00
dependabot[bot]
f492db7976
Bump github.com/onsi/ginkgo/v2 from 2.14.0 to 2.15.0 (#27)
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.14.0 to 2.15.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.14.0...v2.15.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-19 09:02:54 +11:00
dependabot[bot]
bbacde1cec
Bump github.com/onsi/ginkgo/v2 from 2.13.2 to 2.14.0 (#26)
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.13.2 to 2.14.0.
- [Release notes](https://github.com/onsi/ginkgo/releases)
- [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/ginkgo/compare/v2.13.2...v2.14.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/ginkgo/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-12 21:46:31 +11:00
Hidayat
d8a9aeb20e
feat option to draw y or x axis label independently (#25)
Signed-off-by: Hidayat Hamir <hidayat.03@erajaya.com>
2023-12-29 18:51:02 +11:00
Navid Yaghoobi
82465e0608
Merge pull request #24 from navidys/develop
unit tests
2023-12-20 20:43:10 +11:00
Navid Yaghoobi
45b787ac4a unit tests
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2023-12-20 20:40:02 +11:00
Navid Yaghoobi
36cc9e3c83
Merge pull request #23 from navidys/unit_tests
unit tests
2023-12-20 19:32:40 +11:00
Navid Yaghoobi
51013dd682 unit tests
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2023-12-20 19:29:59 +11:00
Navid Yaghoobi
d3cfaf38e9
Merge pull request #22 from navidys/unit_tests
unit test - utils
2023-12-19 18:13:15 +11:00
Navid Yaghoobi
0b5b299832 go update 1.18 + unit tests for utils
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2023-12-19 18:12:08 +11:00
Navid Yaghoobi
0ecdc1cf6f
Merge pull request #21 from navidys/golangci_lint
running golangci-lint
2023-12-15 19:38:50 +11:00
Navid Yaghoobi
02375f4dae initial commit
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2023-12-15 19:37:24 +11:00
Navid Yaghoobi
649e5a318f
Merge pull request #20 from navidys/title_color
add SetTitleColor and SetTitleAlign to dialog and gauge primitives
2023-12-15 19:11:22 +11:00
Navid Yaghoobi
d909054147 add SetTitleColor and SetTitleAlign to dialog and gauge primitives
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2023-12-15 19:10:22 +11:00
Navid Yaghoobi
c7479b197e
Merge pull request #18 from navidys/dependabot/go_modules/github.com/gdamore/tcell/v2-2.7.0
Bump github.com/gdamore/tcell/v2 from 2.6.0 to 2.7.0
2023-12-15 18:01:36 +11:00
dependabot[bot]
bdfbfe7785
Bump github.com/gdamore/tcell/v2 from 2.6.0 to 2.7.0
Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.6.0 to 2.7.0.
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Changelog](https://github.com/gdamore/tcell/blob/main/CHANGESv2.md)
- [Commits](https://github.com/gdamore/tcell/compare/v2.6.0...v2.7.0)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-15 07:01:01 +00:00
Navid Yaghoobi
dd99cf47c5
Merge pull request #19 from ademille/main
Auto set the Y axis label width
2023-12-15 18:00:29 +11:00
Navid Yaghoobi
f2110b65ea running codepsell
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2023-12-15 17:59:47 +11:00
Aaron DeMille
178e77cf79 Auto set the Y axis label width
The Y axis label width was hard coded to 4 with 2 decimal places. This
caused any number larger than 1000 to be truncated.

The Y axis label width is now based on the maximum value in the data
set, allowing enough characters for the whole number along with 2
decimal places.

Signed-off-by: Aaron DeMille <ajdemille@gmail.com>
2023-12-14 23:46:15 -07:00
Navid Yaghoobi
23229b36a7
Merge pull request #17 from ademille/fix-braille-plot
Fix braille plot y-axis scaling
2023-11-24 15:27:40 +11:00
Aaron DeMille
3578e68a90 Fix braille plot y-axis scaling
Braille Mode line charts didn't draw the dots in the correct y-axis
location. For example, I created a chart with data values between 1 and
2 stepping by .1 (1, 1.1, 1.2 ... 2.0). The PlotMarkerDot mode
accurately displayed the dots in the 1 - 2 range on the Y axis. However,
when using PlotMarkerBraille mode, the dots appeared between the range of
0 and 1. The gap is much larger if the the data values are bigger (for
example, 100 to 101 stepping by .1)

I modified the braille plotting code to more closely match the termui
library implementation, which didn't have the problem.

Graphs are now displaying as expected.

Signed-off-by: Aaron DeMille <ajdemille2@gmail.com>
2023-11-23 19:24:14 -07:00
Navid Yaghoobi
a75224ec82 running golangci-lint
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2023-09-17 18:54:59 +10:00
Navid Yaghoobi
1f6c90cf5c
Merge pull request #16 from stdr-sumon/stdr-sumon-patch-1
Update in gauge_um.go
2023-09-17 18:51:53 +10:00
Sumon Sutrodhar
ae559d432d
Update gauge_um.go - will set empty guage with given color
Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com>
2023-08-07 11:46:40 +02:00
Sumon Sutrodhar
17aafbbdb3 This function will set empty guage color with given color
Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com>
2023-08-07 09:43:18 +00:00
Sumon Sutrodhar
077b765b6d
Update gauge_um.go
Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com>
2023-08-07 11:39:10 +02:00
Sumon Sutrodhar
2a46635027
Update gauge_um.go
Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com>
2023-08-07 11:34:26 +02:00
Sumon Sutrodhar
2272eef37b
Update gauge_um.go - Added function will set empty guage color with given color
Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com>
2023-08-07 11:30:19 +02:00
Sumon Sutrodhar
1623e315f8
Update gauge_um.go
This function will set empty guage color with given color

Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com>
2023-08-07 11:25:26 +02:00
Sumon Sutrodhar
ac3d68d3ef
This function will set empty guage color with given color
Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com>
2023-08-07 11:22:08 +02:00
Sumon Sutrodhar
f2924b6431
Update gauge_um.go
This function will set empty guage color with given color

Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com>
2023-08-07 11:20:10 +02:00
Sumon Sutrodhar
f1b3cfcf4d
This function will set empty guage color with given color Signed-off-by: Sumon Sutrodhar <sumon.cse14@gmail.com> 2023-08-07 11:18:13 +02:00
Sumon Sutrodhar
e93d029785
Added function for changing empty guage color
This function will set empty guage color with given color

Signed-off-by: Sumon Sutrodhar <24304603+stdr-sumon@users.noreply.github.com>
2023-08-07 11:10:48 +02:00
Sumon Sutrodhar
060941944a
Signed-off-by: Sumon Sutrodhar <24304603+stdr-sumon@users.noreply.github.com>
Added function for changing empty guage color
2023-08-07 11:02:58 +02:00
Sumon Sutrodhar
63b1d41186
Update gauge_um.go
Added line for DCO check

Signed-off-by: Sumon Sutrodhar <stdr-sumon@users.noreply.github.com>
2023-08-07 10:59:05 +02:00
Sumon Sutrodhar
5b5943da96
Update gauge_um.go
Added line for DCO check
2023-08-07 10:46:46 +02:00
Sumon Sutrodhar
ef53d4c169
Update gauge_um.go
Need this change for a personal project
Signed-off-by: Sumon Sutrodhar (Junor Developer) <sumon94s@hotmail.com>
2023-08-04 11:33:49 +02:00
Sumon Sutrodhar
f74e16ce07
Update gauge_um.go
Need this change for a personal project
2023-08-04 11:22:59 +02:00
Navid Yaghoobi
71c13a7a36
Merge pull request #12 from navidys/dependabot/go_modules/github.com/gdamore/tcell/v2-2.6.0
Bump github.com/gdamore/tcell/v2 from 2.4.1-0.20210905002822-f057f0a857a1 to 2.6.0
2023-02-25 18:30:41 +11:00
dependabot[bot]
1e92566050
Bump github.com/gdamore/tcell/v2
Bumps [github.com/gdamore/tcell/v2](https://github.com/gdamore/tcell) from 2.4.1-0.20210905002822-f057f0a857a1 to 2.6.0.
- [Release notes](https://github.com/gdamore/tcell/releases)
- [Commits](https://github.com/gdamore/tcell/commits/v2.6.0)

---
updated-dependencies:
- dependency-name: github.com/gdamore/tcell/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-24 11:04:18 +00:00
Navid Yaghoobi
25f8729bdf
Merge pull request #10 from navidys/barchart_axis_update
barchart - capability to set axis line and label colors
2022-12-24 12:24:10 +11:00
Navid Yaghoobi
a187d4ddc6 barchart - allow to set axes line and label color
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2022-12-24 12:21:58 +11:00
Navid Yaghoobi
ce6e3eea36
Merge pull request #9 from navidys/demo_update
demo update
2022-12-23 14:15:18 +11:00
Navid Yaghoobi
4339ead54b demo update
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2022-12-23 14:14:09 +11:00
Navid Yaghoobi
4b23c22c64
Merge pull request #8 from Fyb3roptik/fix-misspelling
Fixing mispelling
2022-12-23 07:40:09 +11:00
Nick Wallace
4a0d6f3933 Fixing mispelling 2022-12-22 14:36:57 -06:00
Navid Yaghoobi
759d88ec2f README.md update
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2022-12-17 12:41:00 +11:00
Navid Yaghoobi
48560bcf65
Merge pull request #6 from navidys/sparkline
new widget - sparkline
2022-12-17 12:36:36 +11:00
Navid Yaghoobi
2fead1154d new widget - sparkline
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2022-12-17 12:32:56 +11:00
Navid Yaghoobi
c3592690e1
Merge pull request #5 from navidys/plot_widget
new feature - plot widget (linechart, scatter)
2022-12-13 21:33:25 +11:00
Navid Yaghoobi
b1e54eab00 new feature - plot widget (linechart, scatter)
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2022-12-13 21:30:56 +11:00
Navid Yaghoobi
f9a1efe3f5
Merge pull request #4 from navidys/demo_update
demo update
2022-12-10 17:34:37 +11:00
Navid Yaghoobi
15b85032b3 demo update
Signed-off-by: Navid Yaghoobi <navidys@fedoraproject.org>
2022-12-10 17:33:52 +11:00
46 changed files with 2713 additions and 228 deletions

View File

@ -1,50 +0,0 @@
---
env:
DEST_BRANCH: "main"
CIRRUS_SHELL: "/bin/bash"
timeout_in: 30m
# Run on PRs and main branch post submit only. Don't run tests when tagging.
only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'main')
clone_script: &full_clone |
if [ -z "$CIRRUS_PR" ]; then
git clone --recursive --branch=$CIRRUS_BRANCH https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR
git reset --hard $CIRRUS_CHANGE_IN_REPO
else
git clone --recursive https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR
git fetch origin pull/$CIRRUS_PR/head:pull/$CIRRUS_PR
git reset --hard $CIRRUS_CHANGE_IN_REPO
fi
precommit_test_task:
name: "Precommit"
alias: precommit
clone_script: *full_clone
container:
image: python:3.10
script: |
python3 -m pip install pre-commit
pre-commit run -a
gofmt_task:
name: "Gofmt"
alias: gofmt
clone_script: *full_clone
container:
image: golang:1.18
script: |
SRC=$(find . -type f -name '*.go' -not -path "./vendor/*")
gofmt -w ${SRC}
golangci_lint_task:
name: "Golangci-lint"
alias: lint
clone_script: *full_clone
container:
image: golang:1.18
script: |
make .install.golangci-lint
make lint

49
.github/workflows/go.yml vendored Normal file
View File

@ -0,0 +1,49 @@
name: Go
on:
push:
branches: [ main ]
jobs:
golangci-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '>=1.22'
cache: false
- run: |
make .install.golangci-lint
make lint
unit_test:
name: Unit test
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '>=1.22'
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
export GOBIN=$(pwd)/bin/
make .install.ginkgo
- name: Generate coverage report
run: |
export GOBIN=$(pwd)/bin/
make test-unit
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: .coverage/coverprofile
name: codecov-umbrella
fail_ci_if_error: true
slug: navidys/tvxwidgets
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -32,7 +32,52 @@ jobs:
- uses: codespell-project/actions-codespell@master
with:
check_filenames: true
skip: ./.git,./vendor,*_test.go,go.sum,go.mod
skip: ./.git,./vendor,*_test.go,go.sum,go.mod,*_test.go
golangci-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '>=1.22'
cache: false
- run: |
make .install.golangci-lint
make lint
unit_test:
name: Unit test
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '>=1.22'
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
export GOBIN=$(pwd)/bin/
make .install.ginkgo
- name: Generate coverage report
run: |
export GOBIN=$(pwd)/bin/
make test-unit
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
file: .coverage/coverprofile
name: codecov-umbrella
fail_ci_if_error: false
goreportcard:
name: update reportcard

2
.gitignore vendored
View File

@ -6,7 +6,7 @@
*.dylib
.vscode/*
bin/*
.coverage
# Test binary, built with `go test -c`
*.test

View File

@ -1,21 +1,28 @@
run:
timeout: 10m
deadline: 5m
skip-dirs:
- demos
linters:
enable-all: true
disable:
- exhaustruct
- varnamelen
- exhaustruct
- depguard
# deprecated
- golint
- maligned
- interfacer
- scopelint
- exhaustivestruct
- gomnd
- execinquery
- exportloopref
- rowserrcheck
- wastedassign
linters-settings:
errcheck:
check-blank: false
ignore: fmt:.*
exclude-functions:
- fmt:.*
nolintlint:
require-specific: true
issues:
exclude-dirs:
- demos
exclude-files:
- ".*_test.go"

View File

@ -5,7 +5,7 @@
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

View File

@ -1,6 +1,6 @@
# Contributing To Tvxwidgets
We'd love your contribtion on the project!
We'd love your contribution on the project!
## Developer Certificate of Origin

View File

@ -1,20 +1,29 @@
TARGET := $(shell basename `pwd`)
SRC = $(shell find . -type f -name '*.go' -not -path "./vendor/*")
GO := go
BIN := ./bin
PRE_COMMIT = $(shell command -v bin/venv/bin/pre-commit ~/.local/bin/pre-commit pre-commit | head -n1)
PKG_MANAGER ?= $(shell command -v dnf yum|head -n1)
GINKO_CLI_VERSION = $(shell grep 'ginkgo/v2' go.mod | grep -o ' v.*' | sed 's/ //g' | sed 's|//indirect||g')
COVERAGE_PATH ?= .coverage
#=================================================
# Required tools installation tartgets
#=================================================
.PHONY: install.tools
install.tools: .install.pre-commit .install.codespell .install.golangci-lint ## Install needed tools
install.tools: .install.pre-commit .install.codespell .install.golangci-lint .install.ginkgo ## Install needed tools
.PHONY: .install.codespell
.install.codespell:
sudo ${PKG_MANAGER} -y install codespell
.PHONY: .install.ginkgo
.install.ginkgo:
if [ ! -x "$(GOBIN)/ginkgo" ]; then \
$(GO) install -mod=mod github.com/onsi/ginkgo/v2/ginkgo@$(GINKO_CLI_VERSION) ; \
fi
.PHONY: .install.pre-commit
.install.pre-commit:
if [ -z "$(PRE_COMMIT)" ]; then \
@ -23,7 +32,29 @@ install.tools: .install.pre-commit .install.codespell .install.golangci-lint ##
.PHONY: .install.golangci-lint
.install.golangci-lint:
VERSION=1.46.2 ./hack/install_golangci.sh
VERSION=1.61.0 ./hack/install_golangci.sh
#=================================================
# Testing (units, functionality, ...) targets
#=================================================
.PHONY: test
test: test-unit
.PHONY: test-unit
test-unit: ## Run unit tests
rm -rf ${COVERAGE_PATH} && mkdir -p ${COVERAGE_PATH}
$(GOBIN)/ginkgo \
-r \
--skip-package test/ \
--cover \
--covermode atomic \
--coverprofile coverprofile \
--output-dir ${COVERAGE_PATH} \
--succinct
$(GO) tool cover -html=${COVERAGE_PATH}/coverprofile -o ${COVERAGE_PATH}/coverage.html
$(GO) tool cover -func=${COVERAGE_PATH}/coverprofile > ${COVERAGE_PATH}/functions
cat ${COVERAGE_PATH}/functions | sed -n 's/\(total:\).*\([0-9][0-9].[0-9]\)/\1 \2/p'
#=================================================
# Linting/Formatting/Code Validation targets
@ -58,7 +89,7 @@ govet: ## Run govet
.PHONY: codespell
codespell: ## Run codespell
@echo "running codespell"
@codespell -S ./vendor,go.mod,go.sum,./.git
@codespell -S ./vendor,go.mod,go.sum,./.git,*_test.go
#=================================================
# Help menu

View File

@ -2,13 +2,26 @@
[![PkgGoDev](https://pkg.go.dev/badge/github.com/navidys/tvxwidgets)](https://pkg.go.dev/github.com/navidys/tvxwidgets)
![Go](https://github.com/navidys/tvxwidgets/workflows/Go/badge.svg)
[![codecov](https://codecov.io/gh/navidys/tvxwidgets/branch/main/graph/badge.svg)](https://codecov.io/gh/navidys/tvxwidgets)
[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/navidys/tvxwidgets)
tvxwidgets provides extra widgets for [tview](https://github.com/rivo/tview).
`NOTE:` The project is at its early stages and under development, feel free to contribute and report bugs.
![Screenshot](demo.gif)
## Widgets
* [bar chart](./demos/barchart/)
* [activity mode gauge](./demos/gauge_am/)
* [percentage mode gauge](./demos/gauge_pm/)
* [utilisation mode gauge](./demos/gauge_um/)
* [message dialog (info and error)](./demos/dialog/)
* [spinner](./demos/spinner/)
* [plot (linechart, scatter)](./demos/plot/)
* [sparkline](./demos/sparkline/)
## Example
```go
@ -48,12 +61,3 @@ func main() {
}
```
## Widgets
* bar chart
* activity mode gauge
* percentage mode gauge
* utilisation mode gauge
* message dialog (info and error)
* spinner

View File

@ -1,7 +1,7 @@
package tvxwidgets
import (
"fmt"
"strconv"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
@ -32,15 +32,19 @@ type BarChart struct {
// barWidth width of bars
barWidth int
// hasBorder true if primitive has border
hasBorder bool
hasBorder bool
axesColor tcell.Color
axesLabelColor tcell.Color
}
// NewBarChart returns a new bar chart primitive.
func NewBarChart() *BarChart {
chart := &BarChart{
Box: tview.NewBox(),
barGap: barGap,
barWidth: barWidth,
Box: tview.NewBox(),
barGap: barGap,
barWidth: barWidth,
axesColor: tcell.ColorDimGray,
axesLabelColor: tcell.ColorDimGray,
}
return chart
@ -57,17 +61,14 @@ func (c *BarChart) HasFocus() bool {
}
// Draw draws this primitive onto the screen.
func (c *BarChart) Draw(screen tcell.Screen) { // nolint:funlen,cyclop
style := tcell.StyleDefault
style = style.Foreground(tview.Styles.BorderColor).Background(tview.Styles.PrimitiveBackgroundColor)
func (c *BarChart) Draw(screen tcell.Screen) { //nolint:funlen,cyclop
c.Box.DrawForSubclass(screen, c)
x, y, width, height := c.Box.GetInnerRect()
// log.Printf("%d %d %d %d", x, y, width, height)
maxValY := y + 1
xAxisStartY := y + height - 2 // nolint:gomnd
barStartY := y + height - 3 // nolint:gomnd
xAxisStartY := y + height - 2 //nolint:mnd
barStartY := y + height - 3 //nolint:mnd
borderPadding := 0
if c.hasBorder {
@ -75,26 +76,40 @@ func (c *BarChart) Draw(screen tcell.Screen) { // nolint:funlen,cyclop
}
// set max value if not set
c.initMaxValue()
maxValueSr := fmt.Sprintf("%d", c.maxVal)
maxValueSr := strconv.Itoa(c.maxVal)
maxValLenght := len(maxValueSr) + 1
if maxValLenght < barChartYAxisLabelWidth {
maxValLenght = barChartYAxisLabelWidth
}
// draw graph y-axis
for i := borderPadding; i+y < y+height-borderPadding; i++ {
tview.PrintJoinedSemigraphics(screen, x+maxValLenght, y+i, tview.Borders.Vertical, style)
}
// draw graph x-axix
for i := maxValLenght + 1; i+x < x+width-borderPadding; i++ {
tview.PrintJoinedSemigraphics(screen, x+i, xAxisStartY, tview.Borders.Horizontal, style)
}
tview.PrintJoinedSemigraphics(screen, x+maxValLenght, xAxisStartY, tview.BoxDrawingsLightVerticalAndRight, style)
tview.PrintJoinedSemigraphics(screen, x+maxValLenght-1, xAxisStartY, '0', style)
axesStyle := tcell.StyleDefault.Background(c.GetBackgroundColor()).Foreground(c.axesColor)
axesLabelStyle := tcell.StyleDefault.Background(c.GetBackgroundColor()).Foreground(c.axesLabelColor)
// draw Y axis line
drawLine(screen,
x+maxValLenght,
y+borderPadding,
height-borderPadding-1,
verticalLine, axesStyle)
// draw X axis line
drawLine(screen,
x+maxValLenght+1,
xAxisStartY,
width-borderPadding-maxValLenght-1,
horizontalLine, axesStyle)
tview.PrintJoinedSemigraphics(screen,
x+maxValLenght,
xAxisStartY,
tview.BoxDrawingsLightUpAndRight, axesStyle)
tview.PrintJoinedSemigraphics(screen, x+maxValLenght-1, xAxisStartY, '0', axesLabelStyle)
mxValRune := []rune(maxValueSr)
for i := 0; i < len(mxValRune); i++ {
tview.PrintJoinedSemigraphics(screen, x+borderPadding+i, maxValY, mxValRune[i], style)
for i := range mxValRune {
tview.PrintJoinedSemigraphics(screen, x+borderPadding+i, maxValY, mxValRune[i], axesLabelStyle)
}
// draw bars
@ -108,23 +123,23 @@ func (c *BarChart) Draw(screen tcell.Screen) { // nolint:funlen,cyclop
}
// set labels
r := []rune(item.label)
for j := 0; j < len(r); j++ {
tview.PrintJoinedSemigraphics(screen, startX+j, labelY, r[j], style)
for j := range r {
tview.PrintJoinedSemigraphics(screen, startX+j, labelY, r[j], axesLabelStyle)
}
// bar style
bStyle := style.Foreground(item.color)
bStyle := tcell.StyleDefault.Background(c.GetBackgroundColor()).Foreground(item.color)
barHeight := c.getHeight(valueMaxHeight, item.value)
for k := 0; k < barHeight; k++ {
for l := 0; l < c.barWidth; l++ {
tview.PrintJoinedSemigraphics(screen, startX+l, barStartY-k, '\u2588', bStyle)
for k := range barHeight {
for l := range c.barWidth {
tview.PrintJoinedSemigraphics(screen, startX+l, barStartY-k, fullBlockRune, bStyle)
}
}
// bar value
vSt := fmt.Sprintf("%d", item.value)
vSt := strconv.Itoa(item.value)
vRune := []rune(vSt)
for i := 0; i < len(vRune); i++ {
for i := range vRune {
tview.PrintJoinedSemigraphics(screen, startX+i, barStartY-barHeight, vRune[i], bStyle)
}
@ -155,8 +170,18 @@ func (c *BarChart) SetRect(x, y, width, height int) {
}
// SetMaxValue sets maximum value of bars.
func (c *BarChart) SetMaxValue(max int) {
c.maxVal = max
func (c *BarChart) SetMaxValue(maxValue int) {
c.maxVal = maxValue
}
// SetAxesColor sets axes x and y lines color.
func (c *BarChart) SetAxesColor(color tcell.Color) {
c.axesColor = color
}
// SetAxesLabelColor sets axes x and y label color.
func (c *BarChart) SetAxesLabelColor(color tcell.Color) {
c.axesLabelColor = color
}
// AddBar adds new bar item to the bar chart primitive.
@ -168,9 +193,22 @@ func (c *BarChart) AddBar(label string, value int, color tcell.Color) {
})
}
// RemoveBar removes a bar item from the bar chart.
func (c *BarChart) RemoveBar(label string) {
bars := c.bars[:0]
for _, barItem := range c.bars {
if barItem.label != label {
bars = append(bars, barItem)
}
}
c.bars = bars
}
// SetBarValue sets bar values.
func (c *BarChart) SetBarValue(name string, value int) {
for i := 0; i < len(c.bars); i++ {
for i := range c.bars {
if c.bars[i].label == name {
c.bars[i].value = value
}

67
barchart_test.go Normal file
View File

@ -0,0 +1,67 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
"github.com/navidys/tvxwidgets"
)
var _ = Describe("Barchart", Ordered, func() {
var (
app *tview.Application
headerBox *tview.Box
barchart *tvxwidgets.BarChart
screen tcell.SimulationScreen
)
BeforeAll(func() {
app = tview.NewApplication()
headerBox = tview.NewBox().SetBorder(true)
barchart = tvxwidgets.NewBarChart()
screen = tcell.NewSimulationScreen("UTF-8")
if err := screen.Init(); err != nil {
panic(err)
}
go func() {
appLayout := tview.NewFlex().SetDirection(tview.FlexRow)
appLayout.AddItem(headerBox, 1, 0, true)
appLayout.AddItem(barchart, 50, 0, true)
err := app.SetScreen(screen).SetRoot(appLayout, true).Run()
if err != nil {
panic(err)
}
}()
})
AfterAll(func() {
app.Stop()
})
Describe("Focus", func() {
It("checks primitivie focus", func() {
app.SetFocus(headerBox)
app.Draw()
Expect(barchart.HasFocus()).To(Equal(false))
app.SetFocus(barchart)
app.Draw()
Expect(barchart.HasFocus()).To(Equal(true))
})
})
Describe("GetRect", func() {
It("primitivie size", func() {
x, y, width, heigth := barchart.GetRect()
Expect(x).To(Equal(0))
Expect(y).To(Equal(1))
Expect(width).To(Equal(80))
Expect(heigth).To(Equal(50))
})
})
})

BIN
demo.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 345 KiB

View File

@ -19,6 +19,8 @@ func main() {
barGraph.AddBar("swap", 40, tcell.ColorGreen)
barGraph.AddBar("disk", 40, tcell.ColorOrange)
barGraph.SetMaxValue(100)
barGraph.SetAxesColor(tcell.ColorAntiqueWhite)
barGraph.SetAxesLabelColor(tcell.ColorAntiqueWhite)
if err := app.SetRoot(barGraph, false).EnableMouse(true).Run(); err != nil {
panic(err)

View File

@ -2,6 +2,7 @@
package main
import (
"math"
"math/rand"
"time"
@ -13,15 +14,42 @@ import (
func main() {
app := tview.NewApplication()
// spinners
spinners := []*tvxwidgets.Spinner{
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerDotsCircling),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerDotsUpDown),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerBounce),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerLine),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCircleQuarters),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerSquareCorners),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCircleHalves),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerCorners),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerArrows),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerHamburger),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerStack),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerStar),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerGrowHorizontal),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerGrowVertical),
tvxwidgets.NewSpinner().SetStyle(tvxwidgets.SpinnerBoxBounce),
tvxwidgets.NewSpinner().SetCustomStyle([]rune{'🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚'}),
}
spinnerRow := tview.NewFlex().SetDirection(tview.FlexColumn)
spinnerRow.SetBorder(true).SetTitle("spinners")
for _, spinner := range spinners {
spinnerRow.AddItem(spinner, 0, 1, false)
}
// bar graph
barGraph := tvxwidgets.NewBarChart()
barGraph.SetBorder(true)
barGraph.SetTitle("bar chart")
barGraph.AddBar("eth0", 20, tcell.ColorBlue)
barGraph.AddBar("eth1", 60, tcell.ColorRed)
barGraph.AddBar("eth2", 80, tcell.ColorGreen)
barGraph.AddBar("eth3", 100, tcell.ColorOrange)
barGraph := newBarChart()
barGraph.SetMaxValue(100)
barGraph.SetAxesColor(tcell.ColorAntiqueWhite)
barGraph.SetAxesLabelColor(tcell.ColorAntiqueWhite)
// activity mode gauge
amGauge := tvxwidgets.NewActivityModeGauge()
@ -29,7 +57,7 @@ func main() {
amGauge.SetPgBgColor(tcell.ColorOrange)
amGauge.SetBorder(true)
// percetage mode gauge
// percentage mode gauge
pmGauge := tvxwidgets.NewPercentageModeGauge()
pmGauge.SetTitle("percentage mode gauge")
pmGauge.SetBorder(true)
@ -51,15 +79,6 @@ func main() {
swapGauge.SetLabelColor(tcell.ColorLightSkyBlue)
swapGauge.SetBorder(false)
// dialogs
errDialog := tvxwidgets.NewMessageDialog(tvxwidgets.ErrorDailog)
errDialog.SetTitle("error dialog")
errDialog.SetMessage("This is a sample tvxwidgets error dialog")
msgDialog := tvxwidgets.NewMessageDialog(tvxwidgets.InfoDialog)
msgDialog.SetTitle("message dialog")
msgDialog.SetMessage("[navy::]IMPORTANT MESSAGE[-::]\nThis is a sample tvxwidgets message dialog")
// utilisation flex
utilFlex := tview.NewFlex().SetDirection(tview.FlexRow)
utilFlex.AddItem(cpuGauge, 1, 0, false)
@ -68,20 +87,146 @@ func main() {
utilFlex.SetTitle("utilisation mode gauge")
utilFlex.SetBorder(true)
firstCol := tview.NewFlex().SetDirection(tview.FlexRow)
firstCol.AddItem(barGraph, 11, 0, false)
firstCol.AddItem(msgDialog, 12, 0, true)
// plot (line charts)
sinData := func() [][]float64 {
n := 220
data := make([][]float64, 2)
data[0] = make([]float64, n)
data[1] = make([]float64, n)
for i := 0; i < n; i++ {
data[0][i] = 1 + math.Sin(float64(i)/5)
data[1][i] = 1 + math.Cos(float64(i)/5)
}
return data
}()
secondCol := tview.NewFlex().SetDirection(tview.FlexRow)
secondCol.AddItem(amGauge, 3, 0, false)
secondCol.AddItem(pmGauge, 3, 0, false)
secondCol.AddItem(utilFlex, 5, 0, false)
secondCol.AddItem(errDialog, 12, 0, false)
bmLineChart := newBrailleModeLineChart()
bmLineChart.SetData(sinData)
screenLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
screenLayout.AddItem(firstCol, 50, 0, false)
screenLayout.AddItem(secondCol, 50, 0, false)
dmLineChart := newDotModeLineChart()
sampleData1 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sampleData2 := []float64{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
dotChartData := [][]float64{sampleData1}
dotChartData[0] = append(dotChartData[0], sampleData2...)
dotChartData[0] = append(dotChartData[0], sampleData1[:5]...)
dotChartData[0] = append(dotChartData[0], sampleData2[5:]...)
dotChartData[0] = append(dotChartData[0], sampleData1[:7]...)
dotChartData[0] = append(dotChartData[0], sampleData2[3:]...)
dmLineChart.SetData(dotChartData)
// sparkline
iowaitSparkline := tvxwidgets.NewSparkline()
iowaitSparkline.SetBorder(false)
iowaitSparkline.SetDataTitle("Disk IO (iowait)")
iowaitSparkline.SetDataTitleColor(tcell.ColorDarkOrange)
iowaitSparkline.SetLineColor(tcell.ColorMediumPurple)
systemSparkline := tvxwidgets.NewSparkline()
systemSparkline.SetBorder(false)
systemSparkline.SetDataTitle("Disk IO (system)")
systemSparkline.SetDataTitleColor(tcell.ColorDarkOrange)
systemSparkline.SetLineColor(tcell.ColorSteelBlue)
iowaitData := []float64{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
systemData := []float64{0, 0, 1, 2, 9, 5, 3, 1, 2, 0, 6, 3, 2, 2, 6, 8, 5, 2, 1, 5, 8, 6, 1, 4, 1, 1, 4, 3, 6}
ioSparkLineData := func() []float64 {
for i := 0; i < 5; i++ {
iowaitData = append(iowaitData, iowaitData...)
}
return iowaitData
}()
systemSparklineData := func() []float64 {
for i := 0; i < 5; i++ {
systemData = append(systemData, systemData...)
}
return systemData
}()
iowaitSparkline.SetData(ioSparkLineData)
systemSparkline.SetData(systemSparklineData)
sparklineGroupLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
sparklineGroupLayout.SetBorder(true)
sparklineGroupLayout.SetTitle("sparkline")
sparklineGroupLayout.AddItem(iowaitSparkline, 0, 1, false)
sparklineGroupLayout.AddItem(tview.NewBox(), 1, 0, false)
sparklineGroupLayout.AddItem(systemSparkline, 0, 1, false)
// first row layout
firstRowfirstCol := tview.NewFlex().SetDirection(tview.FlexRow)
firstRowfirstCol.AddItem(barGraph, 0, 1, false)
firstRowSecondCol := tview.NewFlex().SetDirection(tview.FlexRow)
firstRowSecondCol.AddItem(amGauge, 0, 3, false)
firstRowSecondCol.AddItem(pmGauge, 0, 3, false)
firstRowSecondCol.AddItem(utilFlex, 0, 5, false)
firstRow := tview.NewFlex().SetDirection(tview.FlexColumn)
firstRow.AddItem(firstRowfirstCol, 0, 1, false)
firstRow.AddItem(firstRowSecondCol, 0, 1, false)
// second row
plotRowLayout := tview.NewFlex().SetDirection(tview.FlexColumn)
plotRowLayout.AddItem(bmLineChart, 0, 1, false)
plotRowLayout.AddItem(dmLineChart, 0, 1, false)
screenLayout := tview.NewFlex().SetDirection(tview.FlexRow)
screenLayout.AddItem(firstRow, 11, 0, false)
screenLayout.AddItem(plotRowLayout, 15, 0, false)
screenLayout.AddItem(sparklineGroupLayout, 6, 0, false)
screenLayout.AddItem(spinnerRow, 3, 0, false)
screenLayout.SetRect(0, 0, 100, 40)
// upgrade datat functions
moveDotChartData := func() {
newData := append(dotChartData[0], dotChartData[0][0])
dotChartData[0] = newData[1:]
}
moveDiskIOData := func() ([]float64, []float64) {
newIOWaitData := ioSparkLineData[1:]
newIOWaitData = append(newIOWaitData, ioSparkLineData[0])
ioSparkLineData = newIOWaitData
newSystemData := systemSparklineData[1:]
newSystemData = append(newSystemData, systemSparklineData[0])
systemSparklineData = newSystemData
return newIOWaitData, newSystemData
}
moveSinData := func(data [][]float64) [][]float64 {
newData := make([][]float64, 2)
newData[0] = rotate(data[0], -1)
newData[1] = rotate(data[1], -1)
return newData
}
updateSpinner := func() {
spinnerTick := time.NewTicker(100 * time.Millisecond)
for {
select {
case <-spinnerTick.C:
// update spinners
for _, spinner := range spinners {
spinner.Pulse()
}
// update gauge
amGauge.Pulse()
app.Draw()
}
}
}
// update screen ticker
update := func() {
value := 0
maxValue := pmGauge.GetMaxValue()
@ -90,8 +235,6 @@ func main() {
for {
select {
case <-tick.C:
// update gauge
amGauge.Pulse()
if value > maxValue {
value = 0
@ -100,27 +243,99 @@ func main() {
}
pmGauge.SetValue(value)
// update bar graph
rangeLower := 0
rangeUpper := 100
randomNum := rangeLower + rand.Intn(rangeUpper-rangeLower+1)
barGraph.SetBarValue("eth0", randomNum)
cpuGauge.SetValue(float64(randomNum))
randomNum = rangeLower + rand.Intn(rangeUpper-rangeLower+1)
barGraph.SetBarValue("eth1", randomNum)
memGauge.SetValue(float64(randomNum))
randomNum = rangeLower + rand.Intn(rangeUpper-rangeLower+1)
barGraph.SetBarValue("eth2", randomNum)
swapGauge.SetValue(float64(randomNum))
randomNum = rangeLower + rand.Intn(rangeUpper-rangeLower+1)
barGraph.SetBarValue("eth3", randomNum)
randNum1 := float64(rand.Float64() * 100)
randNum2 := float64(rand.Float64() * 100)
randNum3 := float64(rand.Float64() * 100)
randNum4 := float64(rand.Float64() * 100)
barGraph.SetBarValue("eth0", int(randNum1))
cpuGauge.SetValue(randNum1)
barGraph.SetBarValue("eth1", int(randNum2))
memGauge.SetValue(randNum2)
barGraph.SetBarValue("eth2", int(randNum3))
swapGauge.SetValue(randNum3)
barGraph.SetBarValue("eth3", int(randNum4))
// move line charts
sinData = moveSinData(sinData)
bmLineChart.SetData(sinData)
moveDotChartData()
dmLineChart.SetData(dotChartData)
d1, d2 := moveDiskIOData()
iowaitSparkline.SetData(d1)
systemSparkline.SetData(d2)
app.Draw()
}
}
}
go updateSpinner()
go update()
if err := app.SetRoot(screenLayout, false).EnableMouse(true).Run(); err != nil {
panic(err)
}
}
func newDotModeLineChart() *tvxwidgets.Plot {
dmLineChart := tvxwidgets.NewPlot()
dmLineChart.SetBorder(true)
dmLineChart.SetTitle("line chart (dot mode)")
dmLineChart.SetLineColor([]tcell.Color{
tcell.ColorDarkOrange,
})
dmLineChart.SetAxesLabelColor(tcell.ColorGold)
dmLineChart.SetAxesColor(tcell.ColorGold)
dmLineChart.SetMarker(tvxwidgets.PlotMarkerDot)
dmLineChart.SetDotMarkerRune('\u25c9')
return dmLineChart
}
func newBrailleModeLineChart() *tvxwidgets.Plot {
bmLineChart := tvxwidgets.NewPlot()
bmLineChart.SetBorder(true)
bmLineChart.SetTitle("line chart (braille mode)")
bmLineChart.SetLineColor([]tcell.Color{
tcell.ColorSteelBlue,
tcell.ColorGreen,
})
bmLineChart.SetMarker(tvxwidgets.PlotMarkerBraille)
return bmLineChart
}
func newBarChart() *tvxwidgets.BarChart {
barGraph := tvxwidgets.NewBarChart()
barGraph.SetBorder(true)
barGraph.SetTitle("bar chart")
barGraph.AddBar("eth0", 20, tcell.ColorBlue)
barGraph.AddBar("eth1", 60, tcell.ColorRed)
barGraph.AddBar("eth2", 80, tcell.ColorGreen)
barGraph.AddBar("eth3", 100, tcell.ColorOrange)
return barGraph
}
// Source: https://stackoverflow.com/questions/50833673/rotate-array-in-go/79079760#79079760
// rotate rotates the given slice by k positions to the left or right.
func rotate[T any](slice []T, k int) []T {
if len(slice) == 0 {
return slice
}
var r int
if k > 0 {
r = len(slice) - k%len(slice)
} else {
kAbs := int(math.Abs(float64(k)))
r = kAbs % len(slice)
}
slice = append(slice[r:], slice[:r]...)
return slice
}

View File

@ -25,10 +25,8 @@ func main() {
for {
select {
case <-tick.C:
rangeLower := 0
rangeUpper := 100
randomNum := rangeLower + rand.Intn(rangeUpper-rangeLower+1)
gauge.SetValue(float64(randomNum))
randNum := float64(rand.Float64() * 100)
gauge.SetValue(randNum)
app.Draw()
}
}

1
demos/plot/README.md Normal file
View File

@ -0,0 +1 @@
![Screenshot](screenshot.png)

107
demos/plot/main.go Normal file
View File

@ -0,0 +1,107 @@
package main
import (
"math"
"github.com/gdamore/tcell/v2"
"github.com/navidys/tvxwidgets"
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
sinData := func() [][]float64 {
n := 220
data := make([][]float64, 2)
data[0] = make([]float64, n)
data[1] = make([]float64, n)
for i := 0; i < n; i++ {
data[0][i] = 1 + math.Sin(float64(i+1)/5)
// Avoid taking Cos(0) because it creates a high point of 2 that
// will never be hit again and makes the graph look a little funny
data[1][i] = 1 + math.Cos(float64(i+1)/5)
}
return data
}()
bmLineChart := tvxwidgets.NewPlot()
bmLineChart.SetBorder(true)
bmLineChart.SetTitle("line chart (braille mode)")
bmLineChart.SetLineColor([]tcell.Color{
tcell.ColorSteelBlue,
tcell.ColorGreen,
})
bmLineChart.SetMarker(tvxwidgets.PlotMarkerBraille)
bmLineChart.SetData(sinData)
bmLineChart.SetDrawXAxisLabel(false)
dmLineChart := tvxwidgets.NewPlot()
dmLineChart.SetBorder(true)
dmLineChart.SetTitle("line chart (dot mode)")
dmLineChart.SetLineColor([]tcell.Color{
tcell.ColorDarkOrange,
})
dmLineChart.SetAxesLabelColor(tcell.ColorGold)
dmLineChart.SetAxesColor(tcell.ColorGold)
dmLineChart.SetMarker(tvxwidgets.PlotMarkerDot)
dmLineChart.SetDotMarkerRune('\u25c9')
sampleData1 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sampleData2 := []float64{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
dotModeChartData := [][]float64{sampleData1}
dotModeChartData[0] = append(dotModeChartData[0], sampleData2...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData1[:5]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData2[5:]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData1[:7]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData2[3:]...)
dmLineChart.SetData(dotModeChartData)
scatterPlotData := make([][]float64, 2)
scatterPlotData[0] = []float64{1, 2, 3, 4, 5}
scatterPlotData[1] = sinData[1][4:]
dmScatterPlot := tvxwidgets.NewPlot()
dmScatterPlot.SetBorder(true)
dmScatterPlot.SetTitle("scatter plot (dot mode)")
dmScatterPlot.SetLineColor([]tcell.Color{
tcell.ColorMediumSlateBlue,
tcell.ColorLightSkyBlue,
})
dmScatterPlot.SetPlotType(tvxwidgets.PlotTypeScatter)
dmScatterPlot.SetMarker(tvxwidgets.PlotMarkerDot)
dmScatterPlot.SetData(scatterPlotData)
dmScatterPlot.SetDrawYAxisLabel(false)
bmScatterPlot := tvxwidgets.NewPlot()
bmScatterPlot.SetBorder(true)
bmScatterPlot.SetTitle("scatter plot (braille mode)")
bmScatterPlot.SetLineColor([]tcell.Color{
tcell.ColorGold,
tcell.ColorLightSkyBlue,
})
bmScatterPlot.SetPlotType(tvxwidgets.PlotTypeScatter)
bmScatterPlot.SetMarker(tvxwidgets.PlotMarkerBraille)
bmScatterPlot.SetData(scatterPlotData)
firstRow := tview.NewFlex().SetDirection(tview.FlexColumn)
firstRow.AddItem(dmLineChart, 0, 1, false)
firstRow.AddItem(bmLineChart, 0, 1, false)
firstRow.SetRect(0, 0, 100, 15)
secondRow := tview.NewFlex().SetDirection(tview.FlexColumn)
secondRow.AddItem(dmScatterPlot, 0, 1, false)
secondRow.AddItem(bmScatterPlot, 0, 1, false)
secondRow.SetRect(0, 0, 100, 15)
layout := tview.NewFlex().SetDirection(tview.FlexRow)
layout.AddItem(firstRow, 0, 1, false)
layout.AddItem(secondRow, 0, 1, false)
layout.SetRect(0, 0, 100, 30)
if err := app.SetRoot(layout, false).EnableMouse(true).Run(); err != nil {
panic(err)
}
}

BIN
demos/plot/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -0,0 +1 @@
![Screenshot](screenshot.png)

View File

@ -0,0 +1,119 @@
package main
import (
"github.com/gdamore/tcell/v2"
"github.com/navidys/tvxwidgets"
"github.com/rivo/tview"
"math"
)
func main() {
app := tview.NewApplication()
sinData := func() [][]float64 {
n := 220
data := make([][]float64, 2)
data[0] = make([]float64, n)
data[1] = make([]float64, n)
for i := 0; i < n; i++ {
data[0][i] = math.Sin(float64(i+1) / 5)
// Avoid taking Cos(0) because it creates a high point of 2 that
// will never be hit again and makes the graph look a little funny
data[1][i] = math.Cos(float64(i+1) / 5)
}
return data
}()
bmLineChart := tvxwidgets.NewPlot()
bmLineChart.SetBorder(true)
bmLineChart.SetTitle("line chart (braille mode)")
bmLineChart.SetLineColor([]tcell.Color{
tcell.ColorSteelBlue,
tcell.ColorGreen,
})
bmLineChart.SetMarker(tvxwidgets.PlotMarkerBraille)
bmLineChart.SetYAxisAutoScaleMin(false)
bmLineChart.SetYAxisAutoScaleMax(false)
bmLineChart.SetYRange(-1.5, 1.5)
bmLineChart.SetData(sinData)
bmLineChart.SetDrawXAxisLabel(false)
dmLineChart := tvxwidgets.NewPlot()
dmLineChart.SetBorder(true)
dmLineChart.SetTitle("line chart (dot mode)")
dmLineChart.SetLineColor([]tcell.Color{
tcell.ColorDarkOrange,
})
dmLineChart.SetAxesLabelColor(tcell.ColorGold)
dmLineChart.SetAxesColor(tcell.ColorGold)
dmLineChart.SetMarker(tvxwidgets.PlotMarkerDot)
dmLineChart.SetDotMarkerRune('\u25c9')
sampleData1 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
sampleData2 := []float64{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}
dotModeChartData := [][]float64{sampleData1}
dotModeChartData[0] = append(dotModeChartData[0], sampleData2...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData1[:5]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData2[5:]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData1[:7]...)
dotModeChartData[0] = append(dotModeChartData[0], sampleData2[3:]...)
dmLineChart.SetYAxisAutoScaleMin(false)
dmLineChart.SetYAxisAutoScaleMax(false)
dmLineChart.SetYRange(0, 3)
dmLineChart.SetData(dotModeChartData)
scatterPlotData := make([][]float64, 2)
scatterPlotData[0] = []float64{1, 2, 3, 4, 5}
scatterPlotData[1] = sinData[1][4:]
dmScatterPlot := tvxwidgets.NewPlot()
dmScatterPlot.SetBorder(true)
dmScatterPlot.SetTitle("scatter plot (dot mode)")
dmScatterPlot.SetLineColor([]tcell.Color{
tcell.ColorMediumSlateBlue,
tcell.ColorLightSkyBlue,
})
dmScatterPlot.SetPlotType(tvxwidgets.PlotTypeScatter)
dmScatterPlot.SetMarker(tvxwidgets.PlotMarkerDot)
dmScatterPlot.SetYAxisAutoScaleMin(false)
dmScatterPlot.SetYAxisAutoScaleMax(false)
dmScatterPlot.SetYRange(-1, 3)
dmScatterPlot.SetData(scatterPlotData)
dmScatterPlot.SetDrawYAxisLabel(false)
bmScatterPlot := tvxwidgets.NewPlot()
bmScatterPlot.SetBorder(true)
bmScatterPlot.SetTitle("scatter plot (braille mode)")
bmScatterPlot.SetLineColor([]tcell.Color{
tcell.ColorGold,
tcell.ColorLightSkyBlue,
})
bmScatterPlot.SetPlotType(tvxwidgets.PlotTypeScatter)
bmScatterPlot.SetMarker(tvxwidgets.PlotMarkerBraille)
bmScatterPlot.SetYAxisAutoScaleMin(false)
bmScatterPlot.SetYAxisAutoScaleMax(false)
bmScatterPlot.SetYRange(-1, 5)
bmScatterPlot.SetData(scatterPlotData)
firstRow := tview.NewFlex().SetDirection(tview.FlexColumn)
firstRow.AddItem(dmLineChart, 0, 1, false)
firstRow.AddItem(bmLineChart, 0, 1, false)
firstRow.SetRect(0, 0, 100, 15)
secondRow := tview.NewFlex().SetDirection(tview.FlexColumn)
secondRow.AddItem(dmScatterPlot, 0, 1, false)
secondRow.AddItem(bmScatterPlot, 0, 1, false)
secondRow.SetRect(0, 0, 100, 15)
layout := tview.NewFlex().SetDirection(tview.FlexRow)
layout.AddItem(firstRow, 0, 1, false)
layout.AddItem(secondRow, 0, 1, false)
layout.SetRect(0, 0, 100, 30)
if err := app.SetRoot(layout, false).EnableMouse(true).Run(); err != nil {
panic(err)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1 @@
![Screenshot](screenshot.png)

View File

@ -0,0 +1,133 @@
package main
import (
"fmt"
"github.com/gdamore/tcell/v2"
"github.com/navidys/tvxwidgets"
"github.com/rivo/tview"
"math"
"time"
)
func main() {
app := tview.NewApplication()
// >>> Data Function <<<
// With these values, the curve will start with a value of 0 and reach a
// high point of 2 at x = 3.14 (Pi) and then return to 0 at x = 6.28 (2*Pi).
// Play around with these values to get a feel for how they affect the curve
// and how you might adapt this code to plot other functions.
period := 2 * math.Pi
horizontalStretchFactor := 1.0
verticalStretchFactor := 1.0
xOffset := 0.0
yOffset := 0.0
// >>> Graph View/Camera Controls <<<
// These values influence which part of the curve is shown in
// what "zoom level".
xAxisZoomFactor := 3.0
yAxisZoomFactor := 1.0
xAxisShift := 0.0
yAxisShift := 0.0
// xFunc1 defines the x values that should be used for each vertical "slot" in the graph.
xFunc1 := func(i int) float64 {
return (float64(i) / xAxisZoomFactor) + xAxisShift
}
// yFunc1 defines the y values that result from a given input value x (this is the actual function).
yFunc1 := func(x float64) float64 {
return (math.Sin((x+xOffset)/horizontalStretchFactor) + yOffset) * verticalStretchFactor
}
// xLabelFunc1 defines a label for each vertical "slot". Which labels are shown is determined automatically
// based on the available space.
xLabelFunc1 := func(i int) string {
xVal := xFunc1(i)
labelVal := xVal
label := fmt.Sprintf("%.1f", labelVal)
return label
}
// computeDataArray computes the y values for n vertical slots based on the definitions above.
computeDataArray := func() [][]float64 {
n := 150
data := make([][]float64, 1)
data[0] = make([]float64, n)
for i := 0; i < n; i++ {
xVal := xFunc1(i)
yVal := yFunc1(xVal)
data[0][i] = yVal
}
return data
}
data := computeDataArray()
bmLineChart := tvxwidgets.NewPlot()
bmLineChart.SetBorder(true)
bmLineChart.SetTitle("line chart (braille mode)")
bmLineChart.SetLineColor([]tcell.Color{
tcell.ColorSteelBlue,
tcell.ColorGreen,
})
bmLineChart.SetMarker(tvxwidgets.PlotMarkerBraille)
bmLineChart.SetXAxisLabelFunc(xLabelFunc1)
bmLineChart.SetYAxisAutoScaleMin(false)
bmLineChart.SetYAxisAutoScaleMax(false)
bmLineChart.SetYRange(
(-1+yOffset+yAxisShift)/yAxisZoomFactor,
(1+yOffset+yAxisShift)/yAxisZoomFactor,
)
bmLineChart.SetData(data)
firstRow := tview.NewFlex().SetDirection(tview.FlexColumn)
firstRow.AddItem(bmLineChart, 0, 1, false)
firstRow.SetRect(0, 0, 100, 15)
layout := tview.NewFlex().SetDirection(tview.FlexRow)
layout.AddItem(firstRow, 0, 1, false)
layout.SetRect(0, 0, 100, 30)
animate := true
rotateDataContinuously := func() {
tick := time.NewTicker(100 * time.Millisecond)
go func() {
initialxAxisShift := xAxisShift
for {
select {
case <-tick.C:
if !animate {
continue
}
xAxisShift = xAxisShift + 0.1
if xAxisShift >= initialxAxisShift+period*4 {
xAxisShift = initialxAxisShift
}
data = computeDataArray()
bmLineChart.SetData(data)
app.Draw()
}
}
}()
}
go rotateDataContinuously()
if err := app.SetRoot(layout, false).EnableMouse(true).SetMouseCapture(func(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) {
if action == tview.MouseLeftClick {
animate = !animate
}
return event, action
}).Run(); err != nil {
panic(err)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

93
demos/sparkline/main.go Normal file
View File

@ -0,0 +1,93 @@
package main
import (
"time"
"github.com/gdamore/tcell/v2"
"github.com/navidys/tvxwidgets"
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
iowaitSparkline := tvxwidgets.NewSparkline()
iowaitSparkline.SetBorder(false)
iowaitSparkline.SetDataTitle("Disk I/O (iowait)")
iowaitSparkline.SetBorderColor(tcell.ColorDimGray)
iowaitSparkline.SetTitleColor(tcell.ColorDimGray)
iowaitSparkline.SetDataTitleColor(tcell.ColorDarkOrange)
iowaitSparkline.SetLineColor(tcell.ColorMediumPurple)
systemSparkline := tvxwidgets.NewSparkline()
systemSparkline.SetBorder(false)
systemSparkline.SetDataTitle("Disk I/O (system)")
systemSparkline.SetBorderColor(tcell.ColorDimGray)
systemSparkline.SetTitleColor(tcell.ColorDimGray)
systemSparkline.SetDataTitleColor(tcell.ColorDarkOrange)
systemSparkline.SetLineColor(tcell.ColorSteelBlue)
iowaitData := []float64{4, 2, 1, 6, 3, 9, 1, 4, 2, 15, 14, 9, 8, 6, 10, 13, 15, 12, 10, 5, 3, 6, 1, 7, 10, 10, 14, 13, 6}
systemData := []float64{0, 0, 1, 2, 9, 5, 3, 1, 2, 0, 6, 3, 2, 2, 6, 8, 5, 2, 1, 5, 8, 6, 1, 4, 1, 1, 4, 3, 6}
ioSparkLineData := func() []float64 {
for i := 0; i < 5; i++ {
iowaitData = append(iowaitData, iowaitData...)
}
return iowaitData
}()
systemSparklineData := func() []float64 {
for i := 0; i < 5; i++ {
systemData = append(systemData, systemData...)
}
return systemData
}()
iowaitSparkline.SetData(ioSparkLineData)
systemSparkline.SetData(systemSparklineData)
sparklineGroupLayout := tview.NewFlex().SetDirection(tview.FlexRow)
sparklineGroupLayout.SetBorder(true)
sparklineGroupLayout.SetBorderColor(tcell.ColorDimGray)
sparklineGroupLayout.SetTitle("DISK IO")
sparklineGroupLayout.SetTitleColor(tcell.ColorDarkOrange)
sparklineGroupLayout.AddItem(iowaitSparkline, 0, 1, false)
sparklineGroupLayout.AddItem(tview.NewBox(), 1, 0, false)
sparklineGroupLayout.AddItem(systemSparkline, 0, 1, false)
moveData := func() ([]float64, []float64) {
newIOWaitData := ioSparkLineData[1:]
newIOWaitData = append(newIOWaitData, ioSparkLineData[0])
ioSparkLineData = newIOWaitData
newSystemData := systemSparklineData[1:]
newSystemData = append(newSystemData, systemSparklineData[0])
systemSparklineData = newSystemData
return newIOWaitData, newSystemData
}
update := func() {
tick := time.NewTicker(500 * time.Millisecond)
for {
select {
case <-tick.C:
d1, d2 := moveData()
iowaitSparkline.SetData(d1)
systemSparkline.SetData(d2)
app.Draw()
}
}
}
go update()
if err := app.SetRoot(sparklineGroupLayout, true).EnableMouse(true).Run(); err != nil {
panic(err)
}
}

View File

@ -0,0 +1 @@
![Screenshot](screenshot.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -37,7 +37,7 @@ type MessageDialog struct {
bgColor tcell.Color
// message dialog text message to display.
message string
// callback for whwen user clicked on the the button or presses "enter" or "esc"
// callback for when user clicked on the button or presses "enter" or "esc"
doneHandler func()
}
@ -68,18 +68,15 @@ func NewMessageDialog(dtype int) *MessageDialog {
return dialog
}
// SetBorder sets dialogs border - no effect always true.
func (d *MessageDialog) SetBorder(status bool) {}
// SetType sets dialog type to info or error.
func (d *MessageDialog) SetType(dtype int) {
if dtype >= 0 && dtype <= 2 {
if dtype >= 0 && dtype <= 1 {
d.messageType = dtype
d.setColor()
}
}
// SetTitle sets title for this primitive.
// SetTitle sets dialog title.
func (d *MessageDialog) SetTitle(title string) {
d.layout.SetTitle(title)
}
@ -134,13 +131,14 @@ func (d *MessageDialog) Draw(screen tcell.Screen) {
// InputHandler returns input handler function for this primitive.
func (d *MessageDialog) InputHandler() func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
return d.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p tview.Primitive)) {
if event.Key() == tcell.KeyDown || event.Key() == tcell.KeyUp || event.Key() == tcell.KeyPgDn || event.Key() == tcell.KeyPgUp { // nolint:lll
if event.Key() == tcell.KeyDown || event.Key() == tcell.KeyUp || event.Key() == tcell.KeyPgDn || event.Key() == tcell.KeyPgUp { //nolint:lll
if textHandler := d.textview.InputHandler(); textHandler != nil {
textHandler(event, setFocus)
return
}
}
if formHandler := d.form.InputHandler(); formHandler != nil {
formHandler(event, setFocus)
@ -150,12 +148,13 @@ func (d *MessageDialog) InputHandler() func(event *tcell.EventKey, setFocus func
}
// MouseHandler returns the mouse handler for this primitive.
func (d *MessageDialog) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) { // nolint:lll
return d.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) { // nolint:lll,nonamedreturns
func (d *MessageDialog) MouseHandler() func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) { //nolint:lll
return d.WrapMouseHandler(func(action tview.MouseAction, event *tcell.EventMouse, setFocus func(p tview.Primitive)) (consumed bool, capture tview.Primitive) { //nolint:lll,nonamedreturns
// Pass mouse events on to the form.
consumed, capture = d.form.MouseHandler()(action, event, setFocus)
if !consumed && action == tview.MouseLeftClick && d.InRect(event.Position()) {
setFocus(d)
consumed = true
}
@ -164,7 +163,7 @@ func (d *MessageDialog) MouseHandler() func(action tview.MouseAction, event *tce
}
// SetDoneFunc sets callback function for when user clicked on
// the the button or presses "enter" or "esc".
// the button or presses "enter" or "esc".
func (d *MessageDialog) SetDoneFunc(handler func()) *MessageDialog {
d.doneHandler = handler
enterButton := d.form.GetButton(d.form.GetButtonCount() - 1)
@ -173,6 +172,11 @@ func (d *MessageDialog) SetDoneFunc(handler func()) *MessageDialog {
return d
}
// GetBackgroundColor returns dialog background color.
func (d *MessageDialog) GetBackgroundColor() tcell.Color {
return d.bgColor
}
func (d *MessageDialog) setColor() {
var bgColor tcell.Color
@ -186,11 +190,13 @@ func (d *MessageDialog) setColor() {
d.form.SetBackgroundColor(bgColor)
d.textview.SetBackgroundColor(bgColor)
d.layout.SetBackgroundColor(bgColor)
d.bgColor = bgColor
}
func (d *MessageDialog) setRect() {
maxHeight := d.height
maxWidth := d.width // nolint:ifshort
maxWidth := d.width //nolint:ifshort
messageHeight := len(strings.Split(d.message, "\n"))
messageWidth := getMessageWidth(d.message)

73
dialog_test.go Normal file
View File

@ -0,0 +1,73 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
"github.com/navidys/tvxwidgets"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
)
var _ = Describe("Dialog", Ordered, func() {
var (
app *tview.Application
headerBox *tview.Box
msgDialog *tvxwidgets.MessageDialog
screen tcell.SimulationScreen
)
BeforeAll(func() {
app = tview.NewApplication()
headerBox = tview.NewBox().SetBorder(true)
msgDialog = tvxwidgets.NewMessageDialog(tvxwidgets.InfoDialog)
screen = tcell.NewSimulationScreen("UTF-8")
if err := screen.Init(); err != nil {
panic(err)
}
go func() {
appLayout := tview.NewFlex().SetDirection(tview.FlexRow)
appLayout.AddItem(headerBox, 0, 1, true)
appLayout.AddItem(msgDialog, 0, 1, true)
err := app.SetScreen(screen).SetRoot(appLayout, true).Run()
if err != nil {
panic(err)
}
}()
})
AfterAll(func() {
app.Stop()
})
Describe("NewMessageDialog", func() {
It("returns a new message dialog primitive", func() {
tests := []struct {
msgType int
bgColor tcell.Color
}{
{msgType: tvxwidgets.InfoDialog, bgColor: tcell.ColorSteelBlue},
{msgType: tvxwidgets.ErrorDailog, bgColor: tcell.ColorOrangeRed},
}
for _, test := range tests {
msgDialog.SetType(test.msgType)
app.Draw()
Expect(msgDialog.GetBackgroundColor()).To(Equal(test.bgColor))
}
})
})
Describe("Focus", func() {
It("checks primitivie focus", func() {
app.SetFocus(headerBox)
app.Draw()
Expect(msgDialog.HasFocus()).To(Equal(false))
app.SetFocus(msgDialog)
app.Draw()
Expect(msgDialog.HasFocus()).To(Equal(true))
})
})
})

10
export_test.go Normal file
View File

@ -0,0 +1,10 @@
package tvxwidgets
var (
GetColorName = getColorName
GetMessageWidth = getMessageWidth
GetMaxFloat64From2dSlice = getMaxFloat64From2dSlice
GetMaxFloat64FromSlice = getMaxFloat64FromSlice
DrawLine = drawLine
AbsInt = absInt
)

View File

@ -34,18 +34,13 @@ func (g *ActivityModeGauge) Draw(screen tcell.Screen) {
x, y, width, height := g.Box.GetInnerRect()
tickStr := g.tickStr(width)
for i := 0; i < height; i++ {
for i := range height {
tview.Print(screen, tickStr, x, y+i, width, tview.AlignLeft, g.pgBgColor)
}
}
// SetTitle sets title for this primitive.
func (g *ActivityModeGauge) SetTitle(title string) {
g.Box.SetTitle(title)
}
// Focus is called when this primitive receives focus.
func (g *ActivityModeGauge) Focus(delegate func(p tview.Primitive)) {
func (g *ActivityModeGauge) Focus(delegate func(p tview.Primitive)) { //nolint:revive
}
// HasFocus returns whether or not this primitive has focus.
@ -78,27 +73,27 @@ func (g *ActivityModeGauge) Reset() {
g.counter = 0
}
func (g *ActivityModeGauge) tickStr(max int) string {
func (g *ActivityModeGauge) tickStr(maxCount int) string {
var (
prgHeadStr string
prgEndStr string
prgStr string
)
if g.counter >= max-4 {
if g.counter >= maxCount-4 {
g.counter = 0
}
hWidth := 0
for i := 0; i < g.counter; i++ {
for range g.counter {
prgHeadStr += fmt.Sprintf("[%s::]%s", getColorName(tview.Styles.PrimitiveBackgroundColor), prgCell)
hWidth++
}
prgStr = prgCell + prgCell + prgCell + prgCell
for i := 0; i < max+hWidth+4; i++ {
for range maxCount + hWidth + 4 {
prgEndStr += fmt.Sprintf("[%s::]%s", getColorName(tview.Styles.PrimitiveBackgroundColor), prgCell)
}

68
gauge_am_test.go Normal file
View File

@ -0,0 +1,68 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
"github.com/navidys/tvxwidgets"
)
var _ = Describe("GaugeAm", Ordered, func() {
var (
app *tview.Application
headerBox *tview.Box
gaugeAm *tvxwidgets.ActivityModeGauge
screen tcell.SimulationScreen
)
BeforeAll(func() {
app = tview.NewApplication()
headerBox = tview.NewBox().SetBorder(true)
gaugeAm = tvxwidgets.NewActivityModeGauge()
screen = tcell.NewSimulationScreen("UTF-8")
if err := screen.Init(); err != nil {
panic(err)
}
go func() {
appLayout := tview.NewFlex().SetDirection(tview.FlexRow)
appLayout.AddItem(headerBox, 1, 0, true)
appLayout.AddItem(gaugeAm, 50, 0, true)
err := app.SetScreen(screen).SetRoot(appLayout, true).Run()
if err != nil {
panic(err)
}
}()
})
AfterAll(func() {
app.Stop()
})
Describe("Focus", func() {
It("checks primitivie focus", func() {
app.SetFocus(headerBox)
app.Draw()
Expect(gaugeAm.HasFocus()).To(Equal(false))
app.SetFocus(gaugeAm)
gaugeAm.Pulse()
app.Draw()
// gauge will not get focus
Expect(gaugeAm.HasFocus()).To(Equal(false))
})
})
Describe("GetRect", func() {
It("primitivie size", func() {
x, y, width, heigth := gaugeAm.GetRect()
Expect(x).To(Equal(0))
Expect(y).To(Equal(1))
Expect(width).To(Equal(80))
Expect(heigth).To(Equal(50))
})
})
})

View File

@ -47,8 +47,8 @@ func (g *PercentageModeGauge) Draw(screen tcell.Screen) {
prgBlock := g.progressBlock(width)
style := tcell.StyleDefault.Background(g.pgBgColor).Foreground(tview.Styles.PrimaryTextColor)
for i := 0; i < height; i++ {
for j := 0; j < prgBlock; j++ {
for i := range height {
for j := range prgBlock {
screen.SetContent(x+j, y+i, ' ', nil, style)
}
}
@ -56,26 +56,22 @@ func (g *PercentageModeGauge) Draw(screen tcell.Screen) {
// print percentage in middle of box
pcRune := []rune(pcString)
for j := 0; j < len(pcRune); j++ {
for j := range pcRune {
style = tcell.StyleDefault.Background(tview.Styles.PrimitiveBackgroundColor).Foreground(tview.Styles.PrimaryTextColor)
if x+prgBlock >= tX+j {
style = tcell.StyleDefault.Background(g.pgBgColor).Foreground(tview.Styles.PrimaryTextColor)
}
for i := 0; i < height; i++ {
for i := range height {
screen.SetContent(tX+j, y+i, ' ', nil, style)
}
screen.SetContent(tX+j, tY, pcRune[j], nil, style)
}
}
// SetTitle sets title for this primitive.
func (g *PercentageModeGauge) SetTitle(title string) {
g.Box.SetTitle(title)
}
// Focus is called when this primitive receives focus.
func (g *PercentageModeGauge) Focus(delegate func(p tview.Primitive)) {
func (g *PercentageModeGauge) Focus(delegate func(p tview.Primitive)) { //nolint:revive
}
// HasFocus returns whether or not this primitive has focus.
@ -127,13 +123,13 @@ func (g *PercentageModeGauge) Reset() {
g.value = 0
}
func (g *PercentageModeGauge) progressBlock(max int) int {
func (g *PercentageModeGauge) progressBlock(maxValue int) int {
if g.maxValue == 0 {
return g.maxValue
}
pc := g.value * gaugeMaxPc / g.maxValue
value := pc * max / gaugeMaxPc
value := pc * maxValue / gaugeMaxPc
return value
}

67
gauge_pm_test.go Normal file
View File

@ -0,0 +1,67 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
"github.com/navidys/tvxwidgets"
)
var _ = Describe("GaugePm", Ordered, func() {
var (
app *tview.Application
headerBox *tview.Box
gaugePm *tvxwidgets.PercentageModeGauge
screen tcell.SimulationScreen
)
BeforeAll(func() {
app = tview.NewApplication()
headerBox = tview.NewBox().SetBorder(true)
gaugePm = tvxwidgets.NewPercentageModeGauge()
screen = tcell.NewSimulationScreen("UTF-8")
if err := screen.Init(); err != nil {
panic(err)
}
go func() {
appLayout := tview.NewFlex().SetDirection(tview.FlexRow)
appLayout.AddItem(headerBox, 1, 0, true)
appLayout.AddItem(gaugePm, 50, 0, true)
err := app.SetScreen(screen).SetRoot(appLayout, true).Run()
if err != nil {
panic(err)
}
}()
})
AfterAll(func() {
app.Stop()
})
Describe("Focus", func() {
It("checks primitivie focus", func() {
app.SetFocus(headerBox)
app.Draw()
Expect(gaugePm.HasFocus()).To(Equal(false))
app.SetFocus(gaugePm)
app.Draw()
// gauge will not get focus
Expect(gaugePm.HasFocus()).To(Equal(false))
})
})
Describe("GetRect", func() {
It("primitivie size", func() {
x, y, width, heigth := gaugePm.GetRect()
Expect(x).To(Equal(0))
Expect(y).To(Equal(1))
Expect(width).To(Equal(80))
Expect(heigth).To(Equal(50))
})
})
})

View File

@ -48,11 +48,6 @@ func NewUtilModeGauge() *UtilModeGauge {
return gauge
}
// SetTitle sets title for this primitive.
func (g *UtilModeGauge) SetTitle(title string) {
g.Box.SetTitle(title)
}
// SetLabel sets label for this primitive.
func (g *UtilModeGauge) SetLabel(label string) {
g.label = label
@ -64,7 +59,7 @@ func (g *UtilModeGauge) SetLabelColor(color tcell.Color) {
}
// Focus is called when this primitive receives focus.
func (g *UtilModeGauge) Focus(delegate func(p tview.Primitive)) {
func (g *UtilModeGauge) Focus(delegate func(p tview.Primitive)) { //nolint:revive
}
// HasFocus returns whether or not this primitive has focus.
@ -102,8 +97,8 @@ func (g *UtilModeGauge) Draw(screen tcell.Screen) {
labelWidth := len(g.label)
barWidth := width - labelPCWidth - labelWidth
for i := 0; i < barWidth; i++ {
for j := 0; j < height; j++ {
for i := range barWidth {
for j := range height {
value := float64(i * 100 / barWidth)
color := g.getBarColor(value)
@ -152,3 +147,8 @@ func (g *UtilModeGauge) getBarColor(percentage float64) tcell.Color {
return g.critColor
}
// SetEmptyColor sets empty gauge color.
func (g *UtilModeGauge) SetEmptyColor(color tcell.Color) {
g.emptyColor = color
}

67
gauge_um_test.go Normal file
View File

@ -0,0 +1,67 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
"github.com/navidys/tvxwidgets"
)
var _ = Describe("GaugeUm", Ordered, func() {
var (
app *tview.Application
headerBox *tview.Box
gaugeUm *tvxwidgets.UtilModeGauge
screen tcell.SimulationScreen
)
BeforeAll(func() {
app = tview.NewApplication()
headerBox = tview.NewBox().SetBorder(true)
gaugeUm = tvxwidgets.NewUtilModeGauge()
screen = tcell.NewSimulationScreen("UTF-8")
if err := screen.Init(); err != nil {
panic(err)
}
go func() {
appLayout := tview.NewFlex().SetDirection(tview.FlexRow)
appLayout.AddItem(headerBox, 1, 0, true)
appLayout.AddItem(gaugeUm, 50, 0, true)
err := app.SetScreen(screen).SetRoot(appLayout, true).Run()
if err != nil {
panic(err)
}
}()
})
AfterAll(func() {
app.Stop()
})
Describe("Focus", func() {
It("checks primitivie focus", func() {
app.SetFocus(headerBox)
app.Draw()
Expect(gaugeUm.HasFocus()).To(Equal(false))
app.SetFocus(gaugeUm)
app.Draw()
// gauge will not get focus
Expect(gaugeUm.HasFocus()).To(Equal(false))
})
})
Describe("GetRect", func() {
It("primitivie size", func() {
x, y, width, heigth := gaugeUm.GetRect()
Expect(x).To(Equal(0))
Expect(y).To(Equal(1))
Expect(width).To(Equal(80))
Expect(heigth).To(Equal(50))
})
})
})

27
go.mod
View File

@ -1,18 +1,27 @@
module github.com/navidys/tvxwidgets
go 1.17
go 1.22.6
require (
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1
github.com/rivo/tview v0.0.0-20211202162923-2a6de950f73b
github.com/gdamore/tcell/v2 v2.8.1
github.com/onsi/ginkgo/v2 v2.22.2
github.com/onsi/gomega v1.36.2
github.com/rivo/tview v0.0.0-20240616192244-23476fa0bab2
)
require (
github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/encoding v1.0.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.6 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.28.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

117
go.sum
View File

@ -1,22 +1,107 @@
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 h1:QqwPZCwh/k1uYqq6uXSb9TRDhTkfQbO80v8zhnIe5zM=
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=
github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo=
github.com/gdamore/tcell/v2 v2.8.1 h1:KPNxyqclpWpWQlPLx6Xui1pMk8S+7+R37h3g07997NU=
github.com/gdamore/tcell/v2 v2.8.1/go.mod h1:bj8ori1BG3OYMjmb3IklZVWfZUJ1UBQt9JXrOCOhGWw=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/tview v0.0.0-20211202162923-2a6de950f73b h1:EMgbQ+bOHWkl0Ptano8M0yrzVZkxans+Vfv7ox/EtO8=
github.com/rivo/tview v0.0.0-20211202162923-2a6de950f73b/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/tview v0.0.0-20240616192244-23476fa0bab2 h1:LXMiBMxtuXw8e2paN61dI2LMp8JZYyH4UXDwssRI3ys=
github.com/rivo/tview v0.0.0-20240616192244-23476fa0bab2/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

594
plot.go Normal file
View File

@ -0,0 +1,594 @@
package tvxwidgets
import (
"fmt"
"image"
"math"
"strconv"
"strings"
"sync"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
// Marker represents plot drawing marker (braille or dot).
type Marker uint
const (
// plot marker.
PlotMarkerBraille Marker = iota
PlotMarkerDot
)
// PlotYAxisLabelDataType represents plot y axis type (integer or float).
type PlotYAxisLabelDataType uint
const (
PlotYAxisLabelDataInt PlotYAxisLabelDataType = iota
PlotYAxisLabelDataFloat
)
// PlotType represents plot type (line chart or scatter).
type PlotType uint
const (
PlotTypeLineChart PlotType = iota
PlotTypeScatter
)
const (
plotHorizontalScale = 1
plotXAxisLabelsHeight = 1
plotXAxisLabelsGap = 2
plotYAxisLabelsGap = 1
gapRune = " "
)
type brailleCell struct {
cRune rune
color tcell.Color
}
// Plot represents a plot primitive used for different charts.
type Plot struct {
*tview.Box
data [][]float64
// maxVal is the maximum y-axis (vertical) value found in any of the lines in the data set.
maxVal float64
// minVal is the minimum y-axis (vertical) value found in any of the lines in the data set.
minVal float64
marker Marker
ptype PlotType
dotMarkerRune rune
lineColors []tcell.Color
axesColor tcell.Color
axesLabelColor tcell.Color
drawAxes bool
drawXAxisLabel bool
xAxisLabelFunc func(int) string
drawYAxisLabel bool
yAxisLabelDataType PlotYAxisLabelDataType
yAxisAutoScaleMin bool
yAxisAutoScaleMax bool
brailleCellMap map[image.Point]brailleCell
mu sync.Mutex
}
// NewPlot returns a plot widget.
func NewPlot() *Plot {
return &Plot{
Box: tview.NewBox(),
marker: PlotMarkerDot,
ptype: PlotTypeLineChart,
dotMarkerRune: dotRune,
axesColor: tcell.ColorDimGray,
axesLabelColor: tcell.ColorDimGray,
drawAxes: true,
drawXAxisLabel: true,
xAxisLabelFunc: strconv.Itoa,
drawYAxisLabel: true,
yAxisLabelDataType: PlotYAxisLabelDataFloat,
yAxisAutoScaleMin: false,
yAxisAutoScaleMax: true,
lineColors: []tcell.Color{
tcell.ColorSteelBlue,
},
}
}
// Draw draws this primitive onto the screen.
func (plot *Plot) Draw(screen tcell.Screen) {
plot.Box.DrawForSubclass(screen, plot)
switch plot.marker {
case PlotMarkerDot:
plot.drawDotMarkerToScreen(screen)
case PlotMarkerBraille:
plot.drawBrailleMarkerToScreen(screen)
}
plot.drawAxesToScreen(screen)
}
// SetRect sets rect for this primitive.
func (plot *Plot) SetRect(x, y, width, height int) {
plot.Box.SetRect(x, y, width, height)
}
// SetLineColor sets chart line color.
func (plot *Plot) SetLineColor(color []tcell.Color) {
plot.lineColors = color
}
// SetYAxisLabelDataType sets Y axis label data type (integer or float).
func (plot *Plot) SetYAxisLabelDataType(dataType PlotYAxisLabelDataType) {
plot.yAxisLabelDataType = dataType
}
// SetYAxisAutoScaleMin enables YAxis min value autoscale.
func (plot *Plot) SetYAxisAutoScaleMin(autoScale bool) {
plot.yAxisAutoScaleMin = autoScale
}
// SetYAxisAutoScaleMax enables YAxix max value autoscale.
func (plot *Plot) SetYAxisAutoScaleMax(autoScale bool) {
plot.yAxisAutoScaleMax = autoScale
}
// SetAxesColor sets axes x and y lines color.
func (plot *Plot) SetAxesColor(color tcell.Color) {
plot.axesColor = color
}
// SetAxesLabelColor sets axes x and y label color.
func (plot *Plot) SetAxesLabelColor(color tcell.Color) {
plot.axesLabelColor = color
}
// SetDrawAxes set true in order to draw axes to screen.
func (plot *Plot) SetDrawAxes(draw bool) {
plot.drawAxes = draw
}
// SetDrawXAxisLabel set true in order to draw x axis label to screen.
func (plot *Plot) SetDrawXAxisLabel(draw bool) {
plot.drawXAxisLabel = draw
}
// SetXAxisLabelFunc sets x axis label function.
func (plot *Plot) SetXAxisLabelFunc(f func(int) string) {
plot.xAxisLabelFunc = f
}
// SetDrawYAxisLabel set true in order to draw y axis label to screen.
func (plot *Plot) SetDrawYAxisLabel(draw bool) {
plot.drawYAxisLabel = draw
}
// SetMarker sets marker type braille or dot mode.
func (plot *Plot) SetMarker(marker Marker) {
plot.marker = marker
}
// SetPlotType sets plot type (linechart or scatter).
func (plot *Plot) SetPlotType(ptype PlotType) {
plot.ptype = ptype
}
// SetData sets plot data.
func (plot *Plot) SetData(data [][]float64) {
plot.mu.Lock()
defer plot.mu.Unlock()
plot.brailleCellMap = make(map[image.Point]brailleCell)
plot.data = data
if plot.yAxisAutoScaleMax {
plot.maxVal = getMaxFloat64From2dSlice(data)
}
if plot.yAxisAutoScaleMin {
plot.minVal = getMinFloat64From2dSlice(data)
}
}
func (plot *Plot) SetMaxVal(maxVal float64) {
plot.maxVal = maxVal
}
func (plot *Plot) SetMinVal(minVal float64) {
plot.minVal = minVal
}
func (plot *Plot) SetYRange(minVal float64, maxVal float64) {
plot.minVal = minVal
plot.maxVal = maxVal
}
// SetDotMarkerRune sets dot marker rune.
func (plot *Plot) SetDotMarkerRune(r rune) {
plot.dotMarkerRune = r
}
// Figure out the text width necessary to display the largest data value.
func (plot *Plot) getYAxisLabelsWidth() int {
return len(fmt.Sprintf("%.2f", plot.maxVal))
}
// GetPlotRect returns the rect for the inner part of the plot, ie not including axes.
func (plot *Plot) GetPlotRect() (int, int, int, int) {
x, y, width, height := plot.Box.GetInnerRect()
plotYAxisLabelsWidth := plot.getYAxisLabelsWidth()
if plot.drawAxes {
x = x + plotYAxisLabelsWidth + 1
width = width - plotYAxisLabelsWidth - 1
height = height - plotXAxisLabelsHeight - 1
} else {
x++
width--
}
return x, y, width, height
}
func (plot *Plot) getData() [][]float64 {
plot.mu.Lock()
data := plot.data
plot.mu.Unlock()
return data
}
func (plot *Plot) drawAxesToScreen(screen tcell.Screen) {
if !plot.drawAxes {
return
}
x, y, width, height := plot.Box.GetInnerRect()
plotYAxisLabelsWidth := plot.getYAxisLabelsWidth()
axesStyle := tcell.StyleDefault.Background(plot.GetBackgroundColor()).Foreground(plot.axesColor)
// draw Y axis line
drawLine(screen,
x+plotYAxisLabelsWidth,
y,
height-plotXAxisLabelsHeight-1,
verticalLine, axesStyle)
// draw X axis line
drawLine(screen,
x+plotYAxisLabelsWidth+1,
y+height-plotXAxisLabelsHeight-1,
width-plotYAxisLabelsWidth-1,
horizontalLine, axesStyle)
tview.PrintJoinedSemigraphics(screen,
x+plotYAxisLabelsWidth,
y+height-plotXAxisLabelsHeight-1,
tview.BoxDrawingsLightUpAndRight, axesStyle)
if plot.drawXAxisLabel {
plot.drawXAxisLabelsToScreen(screen, plotYAxisLabelsWidth, x, y, width, height)
}
if plot.drawYAxisLabel {
plot.drawYAxisLabelsToScreen(screen, plotYAxisLabelsWidth, x, y, height)
}
}
//nolint:funlen,cyclop
func (plot *Plot) drawXAxisLabelsToScreen(
screen tcell.Screen, plotYAxisLabelsWidth int, x int, y int, width int, height int,
) {
xAxisAreaStartX := x + plotYAxisLabelsWidth + 1
xAxisAreaEndX := x + width
xAxisAvailableWidth := xAxisAreaEndX - xAxisAreaStartX
labelMap := map[int]string{}
labelStartMap := map[int]int{}
maxDataPoints := 0
for _, d := range plot.data {
maxDataPoints = max(maxDataPoints, len(d))
}
// determine the width needed for the largest label
maxXAxisLabelWidth := 0
for _, d := range plot.data {
for i := range d {
label := plot.xAxisLabelFunc(i)
labelMap[i] = label
maxXAxisLabelWidth = max(maxXAxisLabelWidth, len(label))
}
}
// determine the start position for each label, if they were
// to be centered below the data point.
// Note: not all of these labels will be printed, as they would
// overlap with each other
for i, label := range labelMap {
expectedLabelWidth := len(label)
if i == 0 {
expectedLabelWidth += plotXAxisLabelsGap / 2 //nolint:mnd
} else {
expectedLabelWidth += plotXAxisLabelsGap
}
currentLabelStart := i - int(math.Round(float64(expectedLabelWidth)/2)) //nolint:mnd
labelStartMap[i] = currentLabelStart
}
// print the labels, skipping those that would overlap,
// stopping when there is no more space
lastUsedLabelEnd := math.MinInt
initialOffset := xAxisAreaStartX
for i := range maxDataPoints {
labelStart := labelStartMap[i]
if labelStart < lastUsedLabelEnd {
// the label would overlap with the previous label
continue
}
rawLabel := labelMap[i]
labelWithGap := rawLabel
if i == 0 {
labelWithGap += strings.Repeat(gapRune, plotXAxisLabelsGap/2) //nolint:mnd
} else {
labelWithGap = strings.Repeat(gapRune, plotXAxisLabelsGap/2) + labelWithGap + strings.Repeat(gapRune, plotXAxisLabelsGap/2) //nolint:lll,mnd
}
expectedLabelWidth := len(labelWithGap)
remainingWidth := xAxisAvailableWidth - labelStart
if expectedLabelWidth > remainingWidth {
// the label would be too long to fit in the remaining space
if expectedLabelWidth-1 <= remainingWidth {
// if we omit the last gap, it fits, so we draw that before stopping
labelWithoutGap := labelWithGap[:len(labelWithGap)-1]
plot.printXAxisLabel(screen, labelWithoutGap, initialOffset+labelStart, y+height-plotXAxisLabelsHeight)
}
break
}
lastUsedLabelEnd = labelStart + expectedLabelWidth
plot.printXAxisLabel(screen, labelWithGap, initialOffset+labelStart, y+height-plotXAxisLabelsHeight)
}
}
func (plot *Plot) printXAxisLabel(screen tcell.Screen, label string, x, y int) {
tview.Print(screen, label, x, y, len(label), tview.AlignLeft, plot.axesLabelColor)
}
func (plot *Plot) drawYAxisLabelsToScreen(screen tcell.Screen, plotYAxisLabelsWidth int, x int, y int, height int) {
verticalOffset := plot.minVal
verticalScale := (plot.maxVal - plot.minVal) / float64(height-plotXAxisLabelsHeight-1)
previousLabel := ""
for i := 0; i*(plotYAxisLabelsGap+1) < height-1; i++ {
var label string
if plot.yAxisLabelDataType == PlotYAxisLabelDataFloat {
label = fmt.Sprintf("%.2f", float64(i)*verticalScale*(plotYAxisLabelsGap+1)+verticalOffset)
} else {
label = strconv.Itoa(int(float64(i)*verticalScale*(plotYAxisLabelsGap+1) + verticalOffset))
}
// Prevent same label being shown twice.
// Mainly relevant for integer labels with small data sets (in value)
if label == previousLabel {
continue
}
previousLabel = label
tview.Print(screen,
label,
x,
y+height-(i*(plotYAxisLabelsGap+1))-2, //nolint:mnd
plotYAxisLabelsWidth,
tview.AlignLeft, plot.axesLabelColor)
}
}
//nolint:cyclop,gocognit
func (plot *Plot) drawDotMarkerToScreen(screen tcell.Screen) {
x, y, width, height := plot.GetPlotRect()
chartData := plot.getData()
verticalOffset := -plot.minVal
switch plot.ptype {
case PlotTypeLineChart:
for i, line := range chartData {
style := tcell.StyleDefault.Background(plot.GetBackgroundColor()).Foreground(plot.lineColors[i])
for j := 0; j < len(line) && j*plotHorizontalScale < width; j++ {
val := line[j]
if math.IsNaN(val) {
continue
}
lheight := int(((val + verticalOffset) / plot.maxVal) * float64(height-1))
if lheight > height {
continue
}
if (x+(j*plotHorizontalScale) < x+width) && (y+height-1-lheight < y+height) {
tview.PrintJoinedSemigraphics(screen, x+(j*plotHorizontalScale), y+height-1-lheight, plot.dotMarkerRune, style)
}
}
}
case PlotTypeScatter:
for i, line := range chartData {
style := tcell.StyleDefault.Background(plot.GetBackgroundColor()).Foreground(plot.lineColors[i])
for j, val := range line {
if math.IsNaN(val) {
continue
}
lheight := int(((val + verticalOffset) / plot.maxVal) * float64(height-1))
if lheight > height {
continue
}
if (x+(j*plotHorizontalScale) < x+width) && (y+height-1-lheight < y+height) {
tview.PrintJoinedSemigraphics(screen, x+(j*plotHorizontalScale), y+height-1-lheight, plot.dotMarkerRune, style)
}
}
}
}
}
func (plot *Plot) drawBrailleMarkerToScreen(screen tcell.Screen) {
x, y, width, height := plot.GetPlotRect()
plot.calcBrailleLines()
// print to screen
for point, cell := range plot.getBrailleCells() {
style := tcell.StyleDefault.Background(plot.GetBackgroundColor()).Foreground(cell.color)
if point.X < x+width && point.Y < y+height {
tview.PrintJoinedSemigraphics(screen, point.X, point.Y, cell.cRune, style)
}
}
}
func calcDataPointHeight(val, maxVal, minVal float64, height int) int {
return int(((val - minVal) / (maxVal - minVal)) * float64(height-1))
}
func calcDataPointHeightIfInBounds(val float64, maxVal float64, minVal float64, height int) (int, bool) {
if math.IsNaN(val) {
return 0, false
}
result := calcDataPointHeight(val, maxVal, minVal, height)
if (val > maxVal) || (val < minVal) || (result > height) {
return result, false
}
return result, true
}
func (plot *Plot) calcBrailleLines() {
x, y, _, height := plot.GetPlotRect()
chartData := plot.getData()
for i, line := range chartData {
if len(line) <= 1 {
continue
}
previousHeight := 0
lastValWasOk := false
for j, val := range line {
lheight, currentValIsOk := calcDataPointHeightIfInBounds(val, plot.maxVal, plot.minVal, height)
if !lastValWasOk && !currentValIsOk {
// nothing valid to draw, skip to next data point
continue
}
if !lastValWasOk { //nolint:gocritic
// current data point is single valid data point, draw it individually
plot.setBraillePoint(
calcBraillePoint(x, j+1, y, height, lheight),
plot.lineColors[i],
)
} else if !currentValIsOk {
// last data point was single valid data point, draw it individually
plot.setBraillePoint(
calcBraillePoint(x, j, y, height, previousHeight),
plot.lineColors[i],
)
} else {
// we have two valid data points, draw a line between them
plot.setBrailleLine(
calcBraillePoint(x, j, y, height, previousHeight),
calcBraillePoint(x, j+1, y, height, lheight),
plot.lineColors[i],
)
}
lastValWasOk = currentValIsOk
previousHeight = lheight
}
}
}
func calcBraillePoint(x, j, y, maxY, height int) image.Point {
return image.Pt(
(x+(j*plotHorizontalScale))*2, //nolint:mnd
(y+maxY-height-1)*4, //nolint:mnd
)
}
func (plot *Plot) setBraillePoint(p image.Point, color tcell.Color) {
if p.X < 0 || p.Y < 0 {
return
}
point := image.Pt(p.X/2, p.Y/4) //nolint:mnd
plot.brailleCellMap[point] = brailleCell{
plot.brailleCellMap[point].cRune | brailleRune[p.Y%4][p.X%2],
color,
}
}
func (plot *Plot) setBrailleLine(p0, p1 image.Point, color tcell.Color) {
for _, p := range plot.brailleLine(p0, p1) {
plot.setBraillePoint(p, color)
}
}
func (plot *Plot) getBrailleCells() map[image.Point]brailleCell {
cellMap := make(map[image.Point]brailleCell)
for point, cvCell := range plot.brailleCellMap {
cellMap[point] = brailleCell{cvCell.cRune + brailleOffsetRune, cvCell.color}
}
return cellMap
}
func (plot *Plot) brailleLine(p0, p1 image.Point) []image.Point {
points := []image.Point{}
leftPoint, rightPoint := p0, p1
if leftPoint.X > rightPoint.X {
leftPoint, rightPoint = rightPoint, leftPoint
}
xDistance := absInt(leftPoint.X - rightPoint.X)
yDistance := absInt(leftPoint.Y - rightPoint.Y)
slope := float64(yDistance) / float64(xDistance)
slopeSign := 1
if rightPoint.Y < leftPoint.Y {
slopeSign = -1
}
targetYCoordinate := float64(leftPoint.Y)
currentYCoordinate := leftPoint.Y
for i := leftPoint.X; i < rightPoint.X; i++ {
points = append(points, image.Pt(i, currentYCoordinate))
targetYCoordinate += (slope * float64(slopeSign))
for currentYCoordinate != int(targetYCoordinate) {
points = append(points, image.Pt(i, currentYCoordinate))
currentYCoordinate += slopeSign
}
}
return points
}

66
plot_test.go Normal file
View File

@ -0,0 +1,66 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
"github.com/navidys/tvxwidgets"
)
var _ = Describe("Plot", Ordered, func() {
var (
app *tview.Application
headerBox *tview.Box
plot *tvxwidgets.Plot
screen tcell.SimulationScreen
)
BeforeAll(func() {
app = tview.NewApplication()
headerBox = tview.NewBox().SetBorder(true)
plot = tvxwidgets.NewPlot()
screen = tcell.NewSimulationScreen("UTF-8")
if err := screen.Init(); err != nil {
panic(err)
}
go func() {
appLayout := tview.NewFlex().SetDirection(tview.FlexRow)
appLayout.AddItem(headerBox, 1, 0, true)
appLayout.AddItem(plot, 50, 0, true)
err := app.SetScreen(screen).SetRoot(appLayout, true).Run()
if err != nil {
panic(err)
}
}()
})
AfterAll(func() {
app.Stop()
})
Describe("Focus", func() {
It("checks primitivie focus", func() {
app.SetFocus(headerBox)
app.Draw()
Expect(plot.HasFocus()).To(Equal(false))
app.SetFocus(plot)
app.Draw()
Expect(plot.HasFocus()).To(Equal(true))
})
})
Describe("GetRect", func() {
It("primitivie size", func() {
x, y, width, heigth := plot.GetRect()
Expect(x).To(Equal(0))
Expect(y).To(Equal(1))
Expect(width).To(Equal(80))
Expect(heigth).To(Equal(50))
})
})
})

118
sparkline.go Normal file
View File

@ -0,0 +1,118 @@
package tvxwidgets
import (
"math"
"sync"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
// Spartline represents a sparkline widgets.
type Sparkline struct {
*tview.Box
data []float64
dataTitle string
dataTitlecolor tcell.Color
lineColor tcell.Color
mu sync.Mutex
}
// NewSparkline returns a new sparkline widget.
func NewSparkline() *Sparkline {
return &Sparkline{
Box: tview.NewBox(),
}
}
// Draw draws this primitive onto the screen.
func (sl *Sparkline) Draw(screen tcell.Screen) {
sl.Box.DrawForSubclass(screen, sl)
x, y, width, height := sl.Box.GetInnerRect()
barHeight := height
// print label
if sl.dataTitle != "" {
tview.Print(screen, sl.dataTitle, x, y, width, tview.AlignLeft, sl.dataTitlecolor)
barHeight--
}
maxVal := getMaxFloat64FromSlice(sl.data)
if maxVal < 0 {
return
}
// print lines
for i := 0; i < len(sl.data) && i+x < x+width; i++ {
data := sl.data[i]
if math.IsNaN(data) {
continue
}
dHeight := int((data / maxVal) * float64(barHeight))
sparkChar := barsRune[len(barsRune)-1]
style := tcell.StyleDefault.Background(sl.GetBackgroundColor()).Foreground(sl.lineColor)
for j := range dHeight {
tview.PrintJoinedSemigraphics(screen, i+x, y-1+height-j, sparkChar, style)
}
if dHeight == 0 {
sparkChar = barsRune[1]
tview.PrintJoinedSemigraphics(screen, i+x, y-1+height, sparkChar, style)
}
}
}
// SetRect sets rect for this primitive.
func (sl *Sparkline) SetRect(x, y, width, height int) {
sl.Box.SetRect(x, y, width, height)
}
// GetRect return primitive current rect.
func (sl *Sparkline) GetRect() (int, int, int, int) {
return sl.Box.GetRect()
}
// HasFocus returns whether or not this primitive has focus.
func (sl *Sparkline) HasFocus() bool {
return sl.Box.HasFocus()
}
// SetData sets sparkline data.
func (sl *Sparkline) SetData(data []float64) {
sl.mu.Lock()
defer sl.mu.Unlock()
sl.data = data
}
// SetDataTitle sets sparkline data title.
func (sl *Sparkline) SetDataTitle(title string) {
sl.mu.Lock()
defer sl.mu.Unlock()
sl.dataTitle = title
}
// SetDataTitleColor sets sparkline data title color.
func (sl *Sparkline) SetDataTitleColor(color tcell.Color) {
sl.mu.Lock()
defer sl.mu.Unlock()
sl.dataTitlecolor = color
}
// SetLineColor sets sparkline line color.
func (sl *Sparkline) SetLineColor(color tcell.Color) {
sl.mu.Lock()
defer sl.mu.Unlock()
sl.lineColor = color
}

92
sparkline_test.go Normal file
View File

@ -0,0 +1,92 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
"github.com/navidys/tvxwidgets"
)
var _ = Describe("Sparkline", Ordered, func() {
var (
app *tview.Application
headerBox *tview.Box
sparkline *tvxwidgets.Sparkline
screen tcell.SimulationScreen
)
BeforeAll(func() {
app = tview.NewApplication()
headerBox = tview.NewBox().SetBorder(true)
sparkline = tvxwidgets.NewSparkline()
screen = tcell.NewSimulationScreen("UTF-8")
if err := screen.Init(); err != nil {
panic(err)
}
go func() {
appLayout := tview.NewFlex().SetDirection(tview.FlexRow)
appLayout.AddItem(headerBox, 1, 0, true)
appLayout.AddItem(sparkline, 50, 0, true)
err := app.SetScreen(screen).SetRoot(appLayout, true).Run()
if err != nil {
panic(err)
}
}()
})
AfterAll(func() {
app.Stop()
})
Describe("Focus", func() {
It("checks primitivie focus", func() {
app.SetFocus(headerBox)
app.Draw()
Expect(sparkline.HasFocus()).To(Equal(false))
app.SetFocus(sparkline)
app.Draw()
Expect(sparkline.HasFocus()).To(Equal(true))
})
})
Describe("GetRect", func() {
It("primitivie size", func() {
x, y, width, heigth := sparkline.GetRect()
Expect(x).To(Equal(0))
Expect(y).To(Equal(1))
Expect(width).To(Equal(80))
Expect(heigth).To(Equal(50))
})
})
Describe("DataTitle and Color", func() {
It("checks data title text and color", func() {
tests := []struct {
title string
color tcell.Color
}{
{title: "test01", color: tcell.ColorDarkOrange},
{title: "abc123", color: tcell.ColorBlue},
}
for _, test := range tests {
sparkline.SetDataTitle(test.title)
sparkline.SetDataTitleColor(test.color)
app.Draw()
for x := 0; x < len(test.title); x++ {
prune, _, style, _ := screen.GetContent(x, 1)
fg, _, _ := style.Decompose()
Expect(fg).To(Equal(test.color))
Expect(string(prune)).To(Equal(string(test.title[x])))
}
}
})
})
})

104
spinner_test.go Normal file
View File

@ -0,0 +1,104 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
"github.com/navidys/tvxwidgets"
)
var _ = Describe("Spinner", Ordered, func() {
var (
app *tview.Application
headerBox *tview.Box
spinner *tvxwidgets.Spinner
screen tcell.SimulationScreen
)
BeforeAll(func() {
app = tview.NewApplication()
headerBox = tview.NewBox().SetBorder(true)
spinner = tvxwidgets.NewSpinner()
screen = tcell.NewSimulationScreen("UTF-8")
if err := screen.Init(); err != nil {
panic(err)
}
go func() {
appLayout := tview.NewFlex().SetDirection(tview.FlexRow)
appLayout.AddItem(headerBox, 1, 0, true)
appLayout.AddItem(spinner, 50, 0, true)
err := app.SetScreen(screen).SetRoot(appLayout, true).Run()
if err != nil {
panic(err)
}
}()
})
AfterAll(func() {
app.Stop()
})
Describe("Focus", func() {
It("checks primitivie focus", func() {
app.SetFocus(headerBox)
app.Draw()
Expect(spinner.HasFocus()).To(Equal(false))
app.SetFocus(spinner)
app.Draw()
Expect(spinner.HasFocus()).To(Equal(true))
})
})
Describe("GetRect", func() {
It("primitivie size", func() {
x, y, width, heigth := spinner.GetRect()
Expect(x).To(Equal(0))
Expect(y).To(Equal(1))
Expect(width).To(Equal(80))
Expect(heigth).To(Equal(50))
})
})
Describe("Style", func() {
It("checks style", func() {
spinner.SetStyle(tvxwidgets.SpinnerGrowHorizontal)
spinner.Reset()
app.Draw()
prune, _, _, _ := screen.GetContent(0, 1)
Expect(prune).To(Equal('▉'))
spinner.Pulse()
app.Draw()
prune, _, _, _ = screen.GetContent(0, 1)
Expect(prune).To(Equal('▊'))
})
})
Describe("CustomStyle", func() {
It("checks custom style", func() {
customStyle := []rune{'\u2705', '\u274C'}
spinner.SetCustomStyle(customStyle)
spinner.Reset()
app.Draw()
prune, _, _, _ := screen.GetContent(0, 1)
Expect(prune).To(Equal(customStyle[0]))
spinner.Pulse()
app.Draw()
prune, _, _, _ = screen.GetContent(0, 1)
Expect(prune).To(Equal(customStyle[1]))
spinner.Pulse()
app.Draw()
prune, _, _, _ = screen.GetContent(0, 1)
Expect(prune).To(Equal(customStyle[0]))
})
})
})

13
tvxwidgets_suite_test.go Normal file
View File

@ -0,0 +1,13 @@
package tvxwidgets_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestTvxwidgets(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Tvxwidgets Suite")
}

131
utils.go
View File

@ -1,9 +1,18 @@
package tvxwidgets
import (
"math"
"strings"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)
type drawLineMode int
const (
horizontalLine drawLineMode = iota
verticalLine
)
const (
@ -22,7 +31,21 @@ const (
// dialog padding.
dialogPadding = 2
// empty space parts.
emptySpaceParts = 2
emptySpaceParts = 2
brailleOffsetRune = '\u2800'
dotRune = '\u25CF'
fullBlockRune = '\u2588'
)
var (
brailleRune = [4][2]rune{ //nolint:gochecknoglobals
{'\u0001', '\u0008'},
{'\u0002', '\u0010'},
{'\u0004', '\u0020'},
{'\u0040', '\u0080'},
}
barsRune = [...]rune{' ', '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'} //nolint:gochecknoglobals
)
// getColorName returns convert tcell color to its name.
@ -47,3 +70,109 @@ func getMessageWidth(message string) int {
return messageWidth
}
// returns max values in 2D float64 slices.
func getMaxFloat64From2dSlice(slices [][]float64) float64 {
if len(slices) == 0 {
return 0
}
var (
maxValue float64
maxIsInit bool
)
for _, slice := range slices {
for _, val := range slice {
if math.IsNaN(val) {
continue
}
if !maxIsInit {
maxIsInit = true
maxValue = val
continue
}
if val > maxValue {
maxValue = val
}
}
}
return maxValue
}
func getMinFloat64From2dSlice(slices [][]float64) float64 {
if len(slices) == 0 {
return 0
}
var (
minValue float64
minIsInit bool
)
for _, slice := range slices {
for _, val := range slice {
if math.IsNaN(val) {
continue
}
if !minIsInit {
minIsInit = true
minValue = val
continue
}
if val < minValue {
minValue = val
}
}
}
return minValue
}
// returns max values in float64 slices.
func getMaxFloat64FromSlice(slice []float64) float64 {
if len(slice) == 0 {
return 0
}
maxValue := -1.0
for i := range slice {
if math.IsNaN(slice[i]) {
continue
}
if slice[i] > maxValue {
maxValue = slice[i]
}
}
return maxValue
}
func absInt(x int) int {
if x >= 0 {
return x
}
return -x
}
func drawLine(screen tcell.Screen, startX int, startY int, length int, mode drawLineMode, style tcell.Style) {
if mode == horizontalLine {
for i := range length {
tview.PrintJoinedSemigraphics(screen, startX+i, startY, tview.BoxDrawingsLightTripleDashHorizontal, style)
}
} else if mode == verticalLine {
for i := range length {
tview.PrintJoinedSemigraphics(screen, startX, startY+i, tview.BoxDrawingsLightTripleDashVertical, style)
}
}
}

131
utils_test.go Normal file
View File

@ -0,0 +1,131 @@
package tvxwidgets_test
import (
"github.com/gdamore/tcell/v2"
"github.com/navidys/tvxwidgets"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/rivo/tview"
)
var _ = Describe("Utils", func() {
Describe("getColorName", func() {
It("returns color name", func() {
tests := []struct {
color tcell.Color
colorName string
}{
{color: tcell.ColorWhite, colorName: "white"},
{color: tcell.ColorBlack, colorName: "black"},
{color: tcell.NewRGBColor(0, 1, 2), colorName: ""},
}
for _, test := range tests {
Expect(tvxwidgets.GetColorName(test.color)).To(Equal(test.colorName))
}
})
})
Describe("getMessageWidth", func() {
It("returns width size for dialogs based on messages", func() {
tests := []struct {
msg string
width int
}{
{msg: "test", width: 4},
{msg: "test01\ntest001", width: 7},
{msg: "", width: 0},
}
for _, test := range tests {
Expect(tvxwidgets.GetMessageWidth(test.msg)).To(Equal(test.width))
}
})
})
Describe("getMaxFloat64From2dSlice", func() {
It("returns max values in 2D float64 slices.", func() {
tests := []struct {
have [][]float64
wants float64
}{
{have: [][]float64{}, wants: 0},
{have: [][]float64{
{5, -1, 0, -10, 12},
{15, -11, 0, -110, 22},
}, wants: 22},
{have: [][]float64{
{-5, -1, -2, -10, -12},
{-15, -11, -1, -110, -22},
}, wants: -1},
}
for _, test := range tests {
Expect(tvxwidgets.GetMaxFloat64From2dSlice(test.have)).To(Equal(test.wants))
}
})
})
Describe("getMaxFloat64FromSlice", func() {
It("returns max values in float64 slices", func() {
tests := []struct {
have []float64
wants float64
}{
{have: []float64{}, wants: 0},
{have: []float64{5, -1, 0, -10, 12}, wants: 12},
{have: []float64{-10, -20, -9, -1}, wants: -1},
}
for _, test := range tests {
Expect(tvxwidgets.GetMaxFloat64FromSlice(test.have)).To(Equal(test.wants))
}
})
})
Describe("absInt", func() {
It("return absint", func() {
tests := []struct {
have int
wants int
}{
{have: 2, wants: 2},
{have: -2, wants: 2},
{have: 0, wants: 0},
}
for _, test := range tests {
Expect(tvxwidgets.AbsInt(test.have)).To(Equal(test.wants))
}
})
})
Describe("drawLine", func() {
It("draws horizontal or vertival line on screen", func() {
screen := tcell.NewSimulationScreen("UTF-8")
screenWidth := 70
screenHeight := 30
lineStartX := 0
lineStartY := 0
lineLenght := 20
screen.SetSize(screenWidth, screenHeight)
screen.Init()
screen.Clear()
// draw and test horizental line
tvxwidgets.DrawLine(screen, lineStartX, lineStartY, lineLenght, 0, tcell.StyleDefault)
screen.Show()
cellRune, _, _, _ := screen.GetContent(lineStartX, lineStartY)
Expect(cellRune).To(Equal(tview.BoxDrawingsLightTripleDashHorizontal))
// draw and test vertical line
screen.Clear()
tvxwidgets.DrawLine(screen, lineStartX, lineStartY, lineLenght, 1, tcell.StyleDefault)
screen.Show()
cellRune, _, _, _ = screen.GetContent(lineStartX, lineStartY)
Expect(cellRune).To(Equal(tview.BoxDrawingsLightTripleDashVertical))
})
})
})