Codebase list golang-github-gcla-gowid / e729d50
Update upstream source from tag 'upstream/1.4.0' Update to upstream version '1.4.0' with Debian dir 80f57b0bd216bce8edac03f4955d896675d1c8c1 Stephen Gelman 1 year, 7 months ago
126 changed file(s) with 2221 addition(s) and 863 deletion(s). Raw diff Collapse all Expand all
66 runs-on: ubuntu-latest
77 steps:
88
9 - name: Set up Go 1.12
9 - name: Set up Go 1.13
1010 uses: actions/setup-go@v1
1111 with:
12 go-version: 1.12
12 go-version: 1.13
1313 id: go
1414
1515 - name: Check out code into the Go module directory
0 *.log
1 /go.work
2 /go.work.sum
22 env:
33 - GO111MODULE=on
44
5 arch:
6 - amd64
7 - ppc64le
8
59 go:
6 - 1.11.x
7 - 1.12.x
10 - 1.13.x
11 - 1.14.x
12 - 1.15.x
813
914 notifications:
1015 email: true
148148 - [tcell](https://github.com/gdamore/tcell) - a cell based view for text terminals, like xterm, inspired by termbox
149149 - [asciigraph](https://github.com/guptarohit/asciigraph) - lightweight ASCII line-graphs for Go
150150 - [logrus](https://github.com/sirupsen/logrus) - structured pluggable logging for Go
151 - [testify](github.com/stretchr/testify) - tools for testifying that your code will behave as you intend
151 - [testify](https://github.com/stretchr/testify) - tools for testifying that your code will behave as you intend
152152
153153 ## Contact
154154
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1212 "sync"
1313 "time"
1414
15 "github.com/gdamore/tcell"
15 tcell "github.com/gdamore/tcell/v2"
1616 log "github.com/sirupsen/logrus"
1717 )
1818
8181 // provides services to a running gowid application, such as access to the
8282 // palette, the screen and the state of the mouse.
8383 type App struct {
84 IPalette // App holds an IPalette and provides it to each widget when rendering
85 screen tcell.Screen // Each app has one screen
86 TCellEvents chan tcell.Event // Events from tcell e.g. resize
87 AfterRenderEvents chan IAfterRenderEvent // Functions intended to run on the widget goroutine
88 closing bool // If true then app is in process of closing - it may be draining AfterRenderEvents.
89 closingMtx sync.Mutex // Make sure an AfterRenderEvent and closing don't race.
90 viewPlusMenus IWidget // The base widget that is displayed - includes registered menus
91 view IWidget // The base widget that is displayed under registered menus
92 colorMode ColorMode // The current color mode of the terminal - 256, 16, mono, etc
93 inCopyMode bool // True if the app has been switched into "copy mode", for the user to copy a widget value
94 copyClaimed int // True if a widget has "claimed" copy mode during this Render pass
95 copyClaimedBy IIdentity
96 copyLevel int
97 refreshCopy bool
98 prevWasMouseMove bool // True if we last processed simple mouse movement. We can optimize on slow
99 // systems by discarding subsequent mouse movement events.
84 IPalette // App holds an IPalette and provides it to each widget when rendering
85 screen tcell.Screen // Each app has one screen
86 TCellEvents chan tcell.Event // Events from tcell e.g. resize
87 AfterRenderEvents chan IAfterRenderEvent // Functions intended to run on the widget goroutine
88 closing bool // If true then app is in process of closing - it may be draining AfterRenderEvents.
89 closingMtx sync.Mutex // Make sure an AfterRenderEvent and closing don't race.
90 viewPlusMenus IWidget // The base widget that is displayed - includes registered menus
91 view IWidget // The base widget that is displayed under registered menus
92 colorMode ColorMode // The current color mode of the terminal - 256, 16, mono, etc
93 inCopyMode bool // True if the app has been switched into "copy mode", for the user to copy a widget value
94 copyClaimed int // True if a widget has "claimed" copy mode during this Render pass
95 copyClaimedBy IIdentity
96 copyLevel int
97 refreshCopy bool
98 prevWasMouseMove bool // True if we last processed simple mouse movement. We can optimize on slow
99 enableMouseMotion bool
100 enableBracketedPaste bool
101 screenInited bool
102 dontOwnScreen bool
103 tty string
100104
101105 lastMouse MouseState // So I can tell if a button was previously clicked
102106 MouseState // Track which mouse buttons are currently down
108112
109113 // AppArgs is a helper struct, providing arguments for the initialization of App.
110114 type AppArgs struct {
111 View IWidget
112 Palette IPalette
113 Log log.StdLogger
114 DontActivate bool
115 Screen tcell.Screen
116 View IWidget
117 Palette IPalette
118 EnableMouseMotion bool
119 EnableBracketedPaste bool
120 Log log.StdLogger
121 DontActivate bool
122 Tty string
115123 }
116124
117125 // IUnhandledInput is used as a handler for application user input that is not handled by any
230238 }
231239
232240 // NewAppSafe returns an initialized App struct, or an error on failure. It will
233 // initialize a tcell.Screen object behind the scenes, and enable mouse support
241 // initialize a tcell.Screen object and enable mouse support if its not provided,
234242 // meaning that tcell will receive mouse events if the terminal supports them.
235243 func newApp(args AppArgs) (rapp *App, rerr error) {
236 screen, err := tcell.NewScreen()
237 if err != nil {
238 rerr = WithKVs(err, map[string]interface{}{"TERM": os.Getenv("TERM")})
239 return
244 screen := args.Screen
245 if screen == nil {
246 var err error
247 screen, err = tcellScreen(args.Tty)
248 if err != nil {
249 rerr = WithKVs(err, map[string]interface{}{"TERM": os.Getenv("TERM")})
250 return
251 }
240252 }
241253
242254 var palette IPalette = args.Palette
262274 }
263275
264276 res := &App{
265 IPalette: palette,
266 screen: screen,
267 TCellEvents: tch,
268 AfterRenderEvents: wch,
269 closing: false,
270 view: args.View,
271 viewPlusMenus: args.View,
272 colorMode: Mode256Colors,
273 ClickTargets: clicks,
274 log: args.Log,
275 }
276
277 if !args.DontActivate {
278 if err = res.initScreen(); err != nil {
277 IPalette: palette,
278 screen: screen,
279 TCellEvents: tch,
280 AfterRenderEvents: wch,
281 closing: false,
282 view: args.View,
283 viewPlusMenus: args.View,
284 colorMode: Mode256Colors,
285 ClickTargets: clicks,
286 log: args.Log,
287 enableMouseMotion: args.EnableMouseMotion,
288 enableBracketedPaste: args.EnableBracketedPaste,
289 dontOwnScreen: args.Screen != nil,
290 tty: args.Tty,
291 }
292
293 if !res.dontOwnScreen && !args.DontActivate {
294 if err := res.initScreen(); err != nil {
279295 return nil, err
280296 }
281297 res.initColorMode()
298 if res.enableBracketedPaste {
299 screen.EnablePaste()
300 }
282301 }
283302
284303 screen.Clear()
460479 // internal state, like the size of the underlying terminal.
461480 func (a *App) HandleTCellEvent(ev interface{}, unhandled IUnhandledInput) {
462481 switch ev := ev.(type) {
463 case *tcell.EventKey:
482 case *tcell.EventKey, *tcell.EventPaste:
464483 // This makes for a better experience on limited hardware like raspberry pi
465484 debug.SetGCPercent(-1)
466485 defer debug.SetGCPercent(100)
478497 a.refreshCopy = false
479498 }
480499 a.RedrawTerminal()
500
501 //case *tcell.EventPaste:
502 //log.Infof("GCLA: app.go tcell paste")
503
481504 case *tcell.EventMouse:
482 if !a.prevWasMouseMove || ev.Modifiers() != 0 || ev.Buttons() != 0 {
505 if !a.prevWasMouseMove || a.enableMouseMotion || ev.Modifiers() != 0 || ev.Buttons() != 0 {
483506 switch ev.Buttons() {
484507 case tcell.Button1:
485508 a.MouseLeftClicked = true
659682 // currentView's internal buffer is modified if currentView.Editable is true.
660683 func (a *App) handleInputEvent(ev interface{}, unhandled IUnhandledInput) {
661684 switch ev.(type) {
662 case *tcell.EventKey, *tcell.EventMouse:
685 case *tcell.EventKey, *tcell.EventPaste, *tcell.EventMouse:
663686 x, y := a.TerminalSize()
664687 handled := UserInputIfSelectable(a.viewPlusMenus, ev, RenderBox{C: x, R: y}, Focused, a)
665688 if !handled {
791814 // I can't make tcell claim and release the same screen successfully. Clients of
792815 // the app struct shouldn't cache the screen object returned via GetScreen().
793816 //
817 // Assumes we own the screen...
794818 func (a *App) ActivateScreen() error {
795 screen, err := tcell.NewScreen()
819 screen, err := tcellScreen(a.tty)
796820 if err != nil {
797821 return WithKVs(err, map[string]interface{}{"TERM": os.Getenv("TERM")})
798822 }
823 a.DeactivateScreen()
799824 a.screen = screen
800825 if err := a.initScreen(); err != nil {
801826 return err
802827 }
828 if a.enableBracketedPaste {
829 a.screen.EnablePaste()
830 }
803831 return nil
804832 }
805833
834 // Assumes we own the screen
806835 func (a *App) DeactivateScreen() {
807 a.screen.Fini()
808 a.screen = nil
836 if a.screen != nil && a.screenInited {
837 a.screen.Fini()
838 a.screen = nil
839 a.screenInited = false
840 }
809841 }
810842
811843 func (a *App) initScreen() error {
813845 return WithKVs(err, map[string]interface{}{"TERM": os.Getenv("TERM")})
814846 }
815847
848 a.screenInited = true
816849 a.initColorMode()
817850
818851 defFg := ColorDefault
824857 defBg = IColorToTCell(bgCol, defBg, a.GetColorMode())
825858 defSt = defSt.MergeUnder(style)
826859 }
827 defStyle := tcell.Style(defSt.OnOff).Background(defBg.ToTCell()).Foreground(defFg.ToTCell())
860 defStyle := tcell.Style{}.Attributes(defSt.OnOff).Background(defBg.ToTCell()).Foreground(defFg.ToTCell())
828861 // Ask TCell to set the screen's default style according to the palette's "default"
829862 // config, if one is provided. This might make every screen cell underlined, for example,
830863 // in the absence of overriding styling from widgets.
831864 a.screen.SetStyle(defStyle)
832865 a.screen.EnableMouse()
833
866 if a.enableBracketedPaste {
867 a.screen.EnablePaste()
868 }
834869 return nil
835870 }
836871
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package gowid
111111 }
112112
113113 func (c *Callbacks) RemoveCallback(name interface{}, cb IIdentity) bool {
114 if c == nil {
115 return false
116 }
114117 c.Lock()
115118 defer c.Unlock()
116119 cbs, ok := c.callbacks[name]
119122 ok = false
120123 for i, cb2 := range cbs {
121124 if cb.ID() == cb2.ID() {
122 //delete(c.callbacks, name)
123125 // Append backwards for easier deletion later
124126 idxs = append([]int{i}, idxs...)
125127 }
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package gowid
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1010 "unicode/utf8"
1111
1212 "github.com/gcla/gowid/gwutil"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 "github.com/mattn/go-runewidth"
1515 "github.com/pkg/errors"
1616 )
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package gowid
99 "strconv"
1010
1111 "github.com/gcla/gowid/gwutil"
12 "github.com/gdamore/tcell"
12 tcell "github.com/gdamore/tcell/v2"
1313 lru "github.com/hashicorp/golang-lru"
1414 "github.com/lucasb-eyer/go-colorful"
1515 "github.com/pkg/errors"
626626 }
627627
628628 term256 = []TCellColor{
629 MakeTCellColorExt(tcell.Color(0)),
630 MakeTCellColorExt(tcell.Color(1)),
631 MakeTCellColorExt(tcell.Color(2)),
632 MakeTCellColorExt(tcell.Color(3)),
633 MakeTCellColorExt(tcell.Color(4)),
634 MakeTCellColorExt(tcell.Color(5)),
635 MakeTCellColorExt(tcell.Color(6)),
636 MakeTCellColorExt(tcell.Color(7)),
637 MakeTCellColorExt(tcell.Color(8)),
638 MakeTCellColorExt(tcell.Color(9)),
639 MakeTCellColorExt(tcell.Color(10)),
640 MakeTCellColorExt(tcell.Color(11)),
641 MakeTCellColorExt(tcell.Color(12)),
642 MakeTCellColorExt(tcell.Color(13)),
643 MakeTCellColorExt(tcell.Color(14)),
644 MakeTCellColorExt(tcell.Color(15)),
629 MakeTCellColorExt(tcell.ColorBlack),
630 MakeTCellColorExt(tcell.ColorMaroon),
631 MakeTCellColorExt(tcell.ColorGreen),
632 MakeTCellColorExt(tcell.ColorOlive),
633 MakeTCellColorExt(tcell.ColorNavy),
634 MakeTCellColorExt(tcell.ColorPurple),
635 MakeTCellColorExt(tcell.ColorTeal),
636 MakeTCellColorExt(tcell.ColorSilver),
637 MakeTCellColorExt(tcell.ColorGray),
638 MakeTCellColorExt(tcell.ColorRed),
639 MakeTCellColorExt(tcell.ColorLime),
640 MakeTCellColorExt(tcell.ColorYellow),
641 MakeTCellColorExt(tcell.ColorBlue),
642 MakeTCellColorExt(tcell.ColorFuchsia),
643 MakeTCellColorExt(tcell.ColorAqua),
644 MakeTCellColorExt(tcell.ColorWhite),
645645 //
646 MakeTCellColorExt(tcell.Color(16)),
647 MakeTCellColorExt(tcell.Color(17)),
648 MakeTCellColorExt(tcell.Color(18)),
649 MakeTCellColorExt(tcell.Color(19)),
650 MakeTCellColorExt(tcell.Color(20)),
651 MakeTCellColorExt(tcell.Color(21)),
652 MakeTCellColorExt(tcell.Color(22)),
653 MakeTCellColorExt(tcell.Color(23)),
654 MakeTCellColorExt(tcell.Color(24)),
655 MakeTCellColorExt(tcell.Color(25)),
656 MakeTCellColorExt(tcell.Color(26)),
657 MakeTCellColorExt(tcell.Color(27)),
658 MakeTCellColorExt(tcell.Color(28)),
659 MakeTCellColorExt(tcell.Color(29)),
660 MakeTCellColorExt(tcell.Color(30)),
661 MakeTCellColorExt(tcell.Color(31)),
662 MakeTCellColorExt(tcell.Color(32)),
663 MakeTCellColorExt(tcell.Color(33)),
664 MakeTCellColorExt(tcell.Color(34)),
665 MakeTCellColorExt(tcell.Color(35)),
666 MakeTCellColorExt(tcell.Color(36)),
667 MakeTCellColorExt(tcell.Color(37)),
668 MakeTCellColorExt(tcell.Color(38)),
669 MakeTCellColorExt(tcell.Color(39)),
670 MakeTCellColorExt(tcell.Color(40)),
671 MakeTCellColorExt(tcell.Color(41)),
672 MakeTCellColorExt(tcell.Color(42)),
673 MakeTCellColorExt(tcell.Color(43)),
674 MakeTCellColorExt(tcell.Color(44)),
675 MakeTCellColorExt(tcell.Color(45)),
676 MakeTCellColorExt(tcell.Color(46)),
677 MakeTCellColorExt(tcell.Color(47)),
678 MakeTCellColorExt(tcell.Color(48)),
679 MakeTCellColorExt(tcell.Color(49)),
680 MakeTCellColorExt(tcell.Color(50)),
681 MakeTCellColorExt(tcell.Color(51)),
682 MakeTCellColorExt(tcell.Color(52)),
683 MakeTCellColorExt(tcell.Color(53)),
684 MakeTCellColorExt(tcell.Color(54)),
685 MakeTCellColorExt(tcell.Color(55)),
686 MakeTCellColorExt(tcell.Color(56)),
687 MakeTCellColorExt(tcell.Color(57)),
688 MakeTCellColorExt(tcell.Color(58)),
689 MakeTCellColorExt(tcell.Color(59)),
690 MakeTCellColorExt(tcell.Color(60)),
691 MakeTCellColorExt(tcell.Color(61)),
692 MakeTCellColorExt(tcell.Color(62)),
693 MakeTCellColorExt(tcell.Color(63)),
694 MakeTCellColorExt(tcell.Color(64)),
695 MakeTCellColorExt(tcell.Color(65)),
696 MakeTCellColorExt(tcell.Color(66)),
697 MakeTCellColorExt(tcell.Color(67)),
698 MakeTCellColorExt(tcell.Color(68)),
699 MakeTCellColorExt(tcell.Color(69)),
700 MakeTCellColorExt(tcell.Color(70)),
701 MakeTCellColorExt(tcell.Color(71)),
702 MakeTCellColorExt(tcell.Color(72)),
703 MakeTCellColorExt(tcell.Color(73)),
704 MakeTCellColorExt(tcell.Color(74)),
705 MakeTCellColorExt(tcell.Color(75)),
706 MakeTCellColorExt(tcell.Color(76)),
707 MakeTCellColorExt(tcell.Color(77)),
708 MakeTCellColorExt(tcell.Color(78)),
709 MakeTCellColorExt(tcell.Color(79)),
710 MakeTCellColorExt(tcell.Color(80)),
711 MakeTCellColorExt(tcell.Color(81)),
712 MakeTCellColorExt(tcell.Color(82)),
713 MakeTCellColorExt(tcell.Color(83)),
714 MakeTCellColorExt(tcell.Color(84)),
715 MakeTCellColorExt(tcell.Color(85)),
716 MakeTCellColorExt(tcell.Color(86)),
717 MakeTCellColorExt(tcell.Color(87)),
718 MakeTCellColorExt(tcell.Color(88)),
719 MakeTCellColorExt(tcell.Color(89)),
720 MakeTCellColorExt(tcell.Color(90)),
721 MakeTCellColorExt(tcell.Color(91)),
722 MakeTCellColorExt(tcell.Color(92)),
723 MakeTCellColorExt(tcell.Color(93)),
724 MakeTCellColorExt(tcell.Color(94)),
725 MakeTCellColorExt(tcell.Color(95)),
726 MakeTCellColorExt(tcell.Color(96)),
727 MakeTCellColorExt(tcell.Color(97)),
728 MakeTCellColorExt(tcell.Color(98)),
729 MakeTCellColorExt(tcell.Color(99)),
730 MakeTCellColorExt(tcell.Color(100)),
731 MakeTCellColorExt(tcell.Color(101)),
732 MakeTCellColorExt(tcell.Color(102)),
733 MakeTCellColorExt(tcell.Color(103)),
734 MakeTCellColorExt(tcell.Color(104)),
735 MakeTCellColorExt(tcell.Color(105)),
736 MakeTCellColorExt(tcell.Color(106)),
737 MakeTCellColorExt(tcell.Color(107)),
738 MakeTCellColorExt(tcell.Color(108)),
739 MakeTCellColorExt(tcell.Color(109)),
740 MakeTCellColorExt(tcell.Color(110)),
741 MakeTCellColorExt(tcell.Color(111)),
742 MakeTCellColorExt(tcell.Color(112)),
743 MakeTCellColorExt(tcell.Color(113)),
744 MakeTCellColorExt(tcell.Color(114)),
745 MakeTCellColorExt(tcell.Color(115)),
746 MakeTCellColorExt(tcell.Color(116)),
747 MakeTCellColorExt(tcell.Color(117)),
748 MakeTCellColorExt(tcell.Color(118)),
749 MakeTCellColorExt(tcell.Color(119)),
750 MakeTCellColorExt(tcell.Color(120)),
751 MakeTCellColorExt(tcell.Color(121)),
752 MakeTCellColorExt(tcell.Color(122)),
753 MakeTCellColorExt(tcell.Color(123)),
754 MakeTCellColorExt(tcell.Color(124)),
755 MakeTCellColorExt(tcell.Color(125)),
756 MakeTCellColorExt(tcell.Color(126)),
757 MakeTCellColorExt(tcell.Color(127)),
758 MakeTCellColorExt(tcell.Color(128)),
759 MakeTCellColorExt(tcell.Color(129)),
760 MakeTCellColorExt(tcell.Color(130)),
761 MakeTCellColorExt(tcell.Color(131)),
762 MakeTCellColorExt(tcell.Color(132)),
763 MakeTCellColorExt(tcell.Color(133)),
764 MakeTCellColorExt(tcell.Color(134)),
765 MakeTCellColorExt(tcell.Color(135)),
766 MakeTCellColorExt(tcell.Color(136)),
767 MakeTCellColorExt(tcell.Color(137)),
768 MakeTCellColorExt(tcell.Color(138)),
769 MakeTCellColorExt(tcell.Color(139)),
770 MakeTCellColorExt(tcell.Color(140)),
771 MakeTCellColorExt(tcell.Color(141)),
772 MakeTCellColorExt(tcell.Color(142)),
773 MakeTCellColorExt(tcell.Color(143)),
774 MakeTCellColorExt(tcell.Color(144)),
775 MakeTCellColorExt(tcell.Color(145)),
776 MakeTCellColorExt(tcell.Color(146)),
777 MakeTCellColorExt(tcell.Color(147)),
778 MakeTCellColorExt(tcell.Color(148)),
779 MakeTCellColorExt(tcell.Color(149)),
780 MakeTCellColorExt(tcell.Color(150)),
781 MakeTCellColorExt(tcell.Color(151)),
782 MakeTCellColorExt(tcell.Color(152)),
783 MakeTCellColorExt(tcell.Color(153)),
784 MakeTCellColorExt(tcell.Color(154)),
785 MakeTCellColorExt(tcell.Color(155)),
786 MakeTCellColorExt(tcell.Color(156)),
787 MakeTCellColorExt(tcell.Color(157)),
788 MakeTCellColorExt(tcell.Color(158)),
789 MakeTCellColorExt(tcell.Color(159)),
790 MakeTCellColorExt(tcell.Color(160)),
791 MakeTCellColorExt(tcell.Color(161)),
792 MakeTCellColorExt(tcell.Color(162)),
793 MakeTCellColorExt(tcell.Color(163)),
794 MakeTCellColorExt(tcell.Color(164)),
795 MakeTCellColorExt(tcell.Color(165)),
796 MakeTCellColorExt(tcell.Color(166)),
797 MakeTCellColorExt(tcell.Color(167)),
798 MakeTCellColorExt(tcell.Color(168)),
799 MakeTCellColorExt(tcell.Color(169)),
800 MakeTCellColorExt(tcell.Color(170)),
801 MakeTCellColorExt(tcell.Color(171)),
802 MakeTCellColorExt(tcell.Color(172)),
803 MakeTCellColorExt(tcell.Color(173)),
804 MakeTCellColorExt(tcell.Color(174)),
805 MakeTCellColorExt(tcell.Color(175)),
806 MakeTCellColorExt(tcell.Color(176)),
807 MakeTCellColorExt(tcell.Color(177)),
808 MakeTCellColorExt(tcell.Color(178)),
809 MakeTCellColorExt(tcell.Color(179)),
810 MakeTCellColorExt(tcell.Color(180)),
811 MakeTCellColorExt(tcell.Color(181)),
812 MakeTCellColorExt(tcell.Color(182)),
813 MakeTCellColorExt(tcell.Color(183)),
814 MakeTCellColorExt(tcell.Color(184)),
815 MakeTCellColorExt(tcell.Color(185)),
816 MakeTCellColorExt(tcell.Color(186)),
817 MakeTCellColorExt(tcell.Color(187)),
818 MakeTCellColorExt(tcell.Color(188)),
819 MakeTCellColorExt(tcell.Color(189)),
820 MakeTCellColorExt(tcell.Color(190)),
821 MakeTCellColorExt(tcell.Color(191)),
822 MakeTCellColorExt(tcell.Color(192)),
823 MakeTCellColorExt(tcell.Color(193)),
824 MakeTCellColorExt(tcell.Color(194)),
825 MakeTCellColorExt(tcell.Color(195)),
826 MakeTCellColorExt(tcell.Color(196)),
827 MakeTCellColorExt(tcell.Color(197)),
828 MakeTCellColorExt(tcell.Color(198)),
829 MakeTCellColorExt(tcell.Color(199)),
830 MakeTCellColorExt(tcell.Color(200)),
831 MakeTCellColorExt(tcell.Color(201)),
832 MakeTCellColorExt(tcell.Color(202)),
833 MakeTCellColorExt(tcell.Color(203)),
834 MakeTCellColorExt(tcell.Color(204)),
835 MakeTCellColorExt(tcell.Color(205)),
836 MakeTCellColorExt(tcell.Color(206)),
837 MakeTCellColorExt(tcell.Color(207)),
838 MakeTCellColorExt(tcell.Color(208)),
839 MakeTCellColorExt(tcell.Color(209)),
840 MakeTCellColorExt(tcell.Color(210)),
841 MakeTCellColorExt(tcell.Color(211)),
842 MakeTCellColorExt(tcell.Color(212)),
843 MakeTCellColorExt(tcell.Color(213)),
844 MakeTCellColorExt(tcell.Color(214)),
845 MakeTCellColorExt(tcell.Color(215)),
846 MakeTCellColorExt(tcell.Color(216)),
847 MakeTCellColorExt(tcell.Color(217)),
848 MakeTCellColorExt(tcell.Color(218)),
849 MakeTCellColorExt(tcell.Color(219)),
850 MakeTCellColorExt(tcell.Color(220)),
851 MakeTCellColorExt(tcell.Color(221)),
852 MakeTCellColorExt(tcell.Color(222)),
853 MakeTCellColorExt(tcell.Color(223)),
854 MakeTCellColorExt(tcell.Color(224)),
855 MakeTCellColorExt(tcell.Color(225)),
856 MakeTCellColorExt(tcell.Color(226)),
857 MakeTCellColorExt(tcell.Color(227)),
858 MakeTCellColorExt(tcell.Color(228)),
859 MakeTCellColorExt(tcell.Color(229)),
860 MakeTCellColorExt(tcell.Color(230)),
861 MakeTCellColorExt(tcell.Color(231)),
862 MakeTCellColorExt(tcell.Color(232)),
863 MakeTCellColorExt(tcell.Color(233)),
864 MakeTCellColorExt(tcell.Color(234)),
865 MakeTCellColorExt(tcell.Color(235)),
866 MakeTCellColorExt(tcell.Color(236)),
867 MakeTCellColorExt(tcell.Color(237)),
868 MakeTCellColorExt(tcell.Color(238)),
869 MakeTCellColorExt(tcell.Color(239)),
870 MakeTCellColorExt(tcell.Color(240)),
871 MakeTCellColorExt(tcell.Color(241)),
872 MakeTCellColorExt(tcell.Color(242)),
873 MakeTCellColorExt(tcell.Color(243)),
874 MakeTCellColorExt(tcell.Color(244)),
875 MakeTCellColorExt(tcell.Color(245)),
876 MakeTCellColorExt(tcell.Color(246)),
877 MakeTCellColorExt(tcell.Color(247)),
878 MakeTCellColorExt(tcell.Color(248)),
879 MakeTCellColorExt(tcell.Color(249)),
880 MakeTCellColorExt(tcell.Color(250)),
881 MakeTCellColorExt(tcell.Color(251)),
882 MakeTCellColorExt(tcell.Color(252)),
883 MakeTCellColorExt(tcell.Color(253)),
884 MakeTCellColorExt(tcell.Color(254)),
885 MakeTCellColorExt(tcell.Color(255)),
646 MakeTCellColorExt(tcell.Color16),
647 MakeTCellColorExt(tcell.Color17),
648 MakeTCellColorExt(tcell.Color18),
649 MakeTCellColorExt(tcell.Color19),
650 MakeTCellColorExt(tcell.Color20),
651 MakeTCellColorExt(tcell.Color21),
652 MakeTCellColorExt(tcell.Color22),
653 MakeTCellColorExt(tcell.Color23),
654 MakeTCellColorExt(tcell.Color24),
655 MakeTCellColorExt(tcell.Color25),
656 MakeTCellColorExt(tcell.Color26),
657 MakeTCellColorExt(tcell.Color27),
658 MakeTCellColorExt(tcell.Color28),
659 MakeTCellColorExt(tcell.Color29),
660 MakeTCellColorExt(tcell.Color30),
661 MakeTCellColorExt(tcell.Color31),
662 MakeTCellColorExt(tcell.Color32),
663 MakeTCellColorExt(tcell.Color33),
664 MakeTCellColorExt(tcell.Color34),
665 MakeTCellColorExt(tcell.Color35),
666 MakeTCellColorExt(tcell.Color36),
667 MakeTCellColorExt(tcell.Color37),
668 MakeTCellColorExt(tcell.Color38),
669 MakeTCellColorExt(tcell.Color39),
670 MakeTCellColorExt(tcell.Color40),
671 MakeTCellColorExt(tcell.Color41),
672 MakeTCellColorExt(tcell.Color42),
673 MakeTCellColorExt(tcell.Color43),
674 MakeTCellColorExt(tcell.Color44),
675 MakeTCellColorExt(tcell.Color45),
676 MakeTCellColorExt(tcell.Color46),
677 MakeTCellColorExt(tcell.Color47),
678 MakeTCellColorExt(tcell.Color48),
679 MakeTCellColorExt(tcell.Color49),
680 MakeTCellColorExt(tcell.Color50),
681 MakeTCellColorExt(tcell.Color51),
682 MakeTCellColorExt(tcell.Color52),
683 MakeTCellColorExt(tcell.Color53),
684 MakeTCellColorExt(tcell.Color54),
685 MakeTCellColorExt(tcell.Color55),
686 MakeTCellColorExt(tcell.Color56),
687 MakeTCellColorExt(tcell.Color57),
688 MakeTCellColorExt(tcell.Color58),
689 MakeTCellColorExt(tcell.Color59),
690 MakeTCellColorExt(tcell.Color60),
691 MakeTCellColorExt(tcell.Color61),
692 MakeTCellColorExt(tcell.Color62),
693 MakeTCellColorExt(tcell.Color63),
694 MakeTCellColorExt(tcell.Color64),
695 MakeTCellColorExt(tcell.Color65),
696 MakeTCellColorExt(tcell.Color66),
697 MakeTCellColorExt(tcell.Color67),
698 MakeTCellColorExt(tcell.Color68),
699 MakeTCellColorExt(tcell.Color69),
700 MakeTCellColorExt(tcell.Color70),
701 MakeTCellColorExt(tcell.Color71),
702 MakeTCellColorExt(tcell.Color72),
703 MakeTCellColorExt(tcell.Color73),
704 MakeTCellColorExt(tcell.Color74),
705 MakeTCellColorExt(tcell.Color75),
706 MakeTCellColorExt(tcell.Color76),
707 MakeTCellColorExt(tcell.Color77),
708 MakeTCellColorExt(tcell.Color78),
709 MakeTCellColorExt(tcell.Color79),
710 MakeTCellColorExt(tcell.Color80),
711 MakeTCellColorExt(tcell.Color81),
712 MakeTCellColorExt(tcell.Color82),
713 MakeTCellColorExt(tcell.Color83),
714 MakeTCellColorExt(tcell.Color84),
715 MakeTCellColorExt(tcell.Color85),
716 MakeTCellColorExt(tcell.Color86),
717 MakeTCellColorExt(tcell.Color87),
718 MakeTCellColorExt(tcell.Color88),
719 MakeTCellColorExt(tcell.Color89),
720 MakeTCellColorExt(tcell.Color90),
721 MakeTCellColorExt(tcell.Color91),
722 MakeTCellColorExt(tcell.Color92),
723 MakeTCellColorExt(tcell.Color93),
724 MakeTCellColorExt(tcell.Color94),
725 MakeTCellColorExt(tcell.Color95),
726 MakeTCellColorExt(tcell.Color96),
727 MakeTCellColorExt(tcell.Color97),
728 MakeTCellColorExt(tcell.Color98),
729 MakeTCellColorExt(tcell.Color99),
730 MakeTCellColorExt(tcell.Color100),
731 MakeTCellColorExt(tcell.Color101),
732 MakeTCellColorExt(tcell.Color102),
733 MakeTCellColorExt(tcell.Color103),
734 MakeTCellColorExt(tcell.Color104),
735 MakeTCellColorExt(tcell.Color105),
736 MakeTCellColorExt(tcell.Color106),
737 MakeTCellColorExt(tcell.Color107),
738 MakeTCellColorExt(tcell.Color108),
739 MakeTCellColorExt(tcell.Color109),
740 MakeTCellColorExt(tcell.Color110),
741 MakeTCellColorExt(tcell.Color111),
742 MakeTCellColorExt(tcell.Color112),
743 MakeTCellColorExt(tcell.Color113),
744 MakeTCellColorExt(tcell.Color114),
745 MakeTCellColorExt(tcell.Color115),
746 MakeTCellColorExt(tcell.Color116),
747 MakeTCellColorExt(tcell.Color117),
748 MakeTCellColorExt(tcell.Color118),
749 MakeTCellColorExt(tcell.Color119),
750 MakeTCellColorExt(tcell.Color120),
751 MakeTCellColorExt(tcell.Color121),
752 MakeTCellColorExt(tcell.Color122),
753 MakeTCellColorExt(tcell.Color123),
754 MakeTCellColorExt(tcell.Color124),
755 MakeTCellColorExt(tcell.Color125),
756 MakeTCellColorExt(tcell.Color126),
757 MakeTCellColorExt(tcell.Color127),
758 MakeTCellColorExt(tcell.Color128),
759 MakeTCellColorExt(tcell.Color129),
760 MakeTCellColorExt(tcell.Color130),
761 MakeTCellColorExt(tcell.Color131),
762 MakeTCellColorExt(tcell.Color132),
763 MakeTCellColorExt(tcell.Color133),
764 MakeTCellColorExt(tcell.Color134),
765 MakeTCellColorExt(tcell.Color135),
766 MakeTCellColorExt(tcell.Color136),
767 MakeTCellColorExt(tcell.Color137),
768 MakeTCellColorExt(tcell.Color138),
769 MakeTCellColorExt(tcell.Color139),
770 MakeTCellColorExt(tcell.Color140),
771 MakeTCellColorExt(tcell.Color141),
772 MakeTCellColorExt(tcell.Color142),
773 MakeTCellColorExt(tcell.Color143),
774 MakeTCellColorExt(tcell.Color144),
775 MakeTCellColorExt(tcell.Color145),
776 MakeTCellColorExt(tcell.Color146),
777 MakeTCellColorExt(tcell.Color147),
778 MakeTCellColorExt(tcell.Color148),
779 MakeTCellColorExt(tcell.Color149),
780 MakeTCellColorExt(tcell.Color150),
781 MakeTCellColorExt(tcell.Color151),
782 MakeTCellColorExt(tcell.Color152),
783 MakeTCellColorExt(tcell.Color153),
784 MakeTCellColorExt(tcell.Color154),
785 MakeTCellColorExt(tcell.Color155),
786 MakeTCellColorExt(tcell.Color156),
787 MakeTCellColorExt(tcell.Color157),
788 MakeTCellColorExt(tcell.Color158),
789 MakeTCellColorExt(tcell.Color159),
790 MakeTCellColorExt(tcell.Color160),
791 MakeTCellColorExt(tcell.Color161),
792 MakeTCellColorExt(tcell.Color162),
793 MakeTCellColorExt(tcell.Color163),
794 MakeTCellColorExt(tcell.Color164),
795 MakeTCellColorExt(tcell.Color165),
796 MakeTCellColorExt(tcell.Color166),
797 MakeTCellColorExt(tcell.Color167),
798 MakeTCellColorExt(tcell.Color168),
799 MakeTCellColorExt(tcell.Color169),
800 MakeTCellColorExt(tcell.Color170),
801 MakeTCellColorExt(tcell.Color171),
802 MakeTCellColorExt(tcell.Color172),
803 MakeTCellColorExt(tcell.Color173),
804 MakeTCellColorExt(tcell.Color174),
805 MakeTCellColorExt(tcell.Color175),
806 MakeTCellColorExt(tcell.Color176),
807 MakeTCellColorExt(tcell.Color177),
808 MakeTCellColorExt(tcell.Color178),
809 MakeTCellColorExt(tcell.Color179),
810 MakeTCellColorExt(tcell.Color180),
811 MakeTCellColorExt(tcell.Color181),
812 MakeTCellColorExt(tcell.Color182),
813 MakeTCellColorExt(tcell.Color183),
814 MakeTCellColorExt(tcell.Color184),
815 MakeTCellColorExt(tcell.Color185),
816 MakeTCellColorExt(tcell.Color186),
817 MakeTCellColorExt(tcell.Color187),
818 MakeTCellColorExt(tcell.Color188),
819 MakeTCellColorExt(tcell.Color189),
820 MakeTCellColorExt(tcell.Color190),
821 MakeTCellColorExt(tcell.Color191),
822 MakeTCellColorExt(tcell.Color192),
823 MakeTCellColorExt(tcell.Color193),
824 MakeTCellColorExt(tcell.Color194),
825 MakeTCellColorExt(tcell.Color195),
826 MakeTCellColorExt(tcell.Color196),
827 MakeTCellColorExt(tcell.Color197),
828 MakeTCellColorExt(tcell.Color198),
829 MakeTCellColorExt(tcell.Color199),
830 MakeTCellColorExt(tcell.Color200),
831 MakeTCellColorExt(tcell.Color201),
832 MakeTCellColorExt(tcell.Color202),
833 MakeTCellColorExt(tcell.Color203),
834 MakeTCellColorExt(tcell.Color204),
835 MakeTCellColorExt(tcell.Color205),
836 MakeTCellColorExt(tcell.Color206),
837 MakeTCellColorExt(tcell.Color207),
838 MakeTCellColorExt(tcell.Color208),
839 MakeTCellColorExt(tcell.Color209),
840 MakeTCellColorExt(tcell.Color210),
841 MakeTCellColorExt(tcell.Color211),
842 MakeTCellColorExt(tcell.Color212),
843 MakeTCellColorExt(tcell.Color213),
844 MakeTCellColorExt(tcell.Color214),
845 MakeTCellColorExt(tcell.Color215),
846 MakeTCellColorExt(tcell.Color216),
847 MakeTCellColorExt(tcell.Color217),
848 MakeTCellColorExt(tcell.Color218),
849 MakeTCellColorExt(tcell.Color219),
850 MakeTCellColorExt(tcell.Color220),
851 MakeTCellColorExt(tcell.Color221),
852 MakeTCellColorExt(tcell.Color222),
853 MakeTCellColorExt(tcell.Color223),
854 MakeTCellColorExt(tcell.Color224),
855 MakeTCellColorExt(tcell.Color225),
856 MakeTCellColorExt(tcell.Color226),
857 MakeTCellColorExt(tcell.Color227),
858 MakeTCellColorExt(tcell.Color228),
859 MakeTCellColorExt(tcell.Color229),
860 MakeTCellColorExt(tcell.Color230),
861 MakeTCellColorExt(tcell.Color231),
862 MakeTCellColorExt(tcell.Color232),
863 MakeTCellColorExt(tcell.Color233),
864 MakeTCellColorExt(tcell.Color234),
865 MakeTCellColorExt(tcell.Color235),
866 MakeTCellColorExt(tcell.Color236),
867 MakeTCellColorExt(tcell.Color237),
868 MakeTCellColorExt(tcell.Color238),
869 MakeTCellColorExt(tcell.Color239),
870 MakeTCellColorExt(tcell.Color240),
871 MakeTCellColorExt(tcell.Color241),
872 MakeTCellColorExt(tcell.Color242),
873 MakeTCellColorExt(tcell.Color243),
874 MakeTCellColorExt(tcell.Color244),
875 MakeTCellColorExt(tcell.Color245),
876 MakeTCellColorExt(tcell.Color246),
877 MakeTCellColorExt(tcell.Color247),
878 MakeTCellColorExt(tcell.Color248),
879 MakeTCellColorExt(tcell.Color249),
880 MakeTCellColorExt(tcell.Color250),
881 MakeTCellColorExt(tcell.Color251),
882 MakeTCellColorExt(tcell.Color252),
883 MakeTCellColorExt(tcell.Color253),
884 MakeTCellColorExt(tcell.Color254),
885 MakeTCellColorExt(tcell.Color255),
886886 }
887887
888888 term2Cache *lru.Cache
10121012 bgt = bg.ToTCell()
10131013 }
10141014 st := StyleNone.MergeUnder(attr)
1015 return tcell.Style(st.OnOff).Foreground(fgt).Background(bgt)
1015 return tcell.Style{}.Attributes(st.OnOff).Foreground(fgt).Background(bgt)
10161016 }
10171017
10181018 //======================================================================
12051205 func (r RGBColor) ToTCellColor(mode ColorMode) (TCellColor, bool) {
12061206 switch mode {
12071207 case Mode24BitColors:
1208 c := tcell.Color((r.Red << 16) | (r.Green << 8) | (r.Blue << 0) | int(tcell.ColorIsRGB))
1208 c := tcell.NewRGBColor(int32(r.Red), int32(r.Green), int32(r.Blue))
12091209 return MakeTCellColorExt(c), true
12101210 case Mode256Colors:
12111211 if IgnoreBase16 {
12171217 rd := cubeLookup88_16[r.Red>>4]
12181218 g := cubeLookup88_16[r.Green>>4]
12191219 b := cubeLookup88_16[r.Blue>>4]
1220 c := tcell.Color((CubeStart + (((rd * cubeSize88) + g) * cubeSize88) + b) + 0)
1220 c := tcell.Color((CubeStart + (((rd * cubeSize88) + g) * cubeSize88) + b) + 0) + tcell.ColorValid
12211221 return MakeTCellColorExt(c), true
12221222 case Mode16Colors:
12231223 return r.findClosest(colorful16, term16, term16Cache), true
12991299 panic(errors.WithStack(InvalidColor{Color: s}))
13001300 }
13011301
1302 idx = idx - 1 // offset for tcell, which stores default at -1
1303
1304 c := MakeTCellColorExt(tcell.Color(idx))
1302 col := tcell.ColorDefault
1303 if idx > 0 {
1304 idx = idx - 1
1305 col = tcell.ColorValid + tcell.Color(idx)
1306 }
1307 c := MakeTCellColorExt(col)
13051308
13061309 switch mode {
13071310 case Mode24BitColors, Mode256Colors, Mode88Colors, Mode16Colors:
13911394 switch mode {
13921395 case Mode24BitColors:
13931396 adj := intScale(s.Val, 101, 0x100)
1394 c := tcell.Color((adj << 16) | (adj << 8) | (adj << 0) | int(tcell.ColorIsRGB))
1397 c := tcell.NewRGBColor(int32(adj), int32(adj), int32(adj))
13951398 return MakeTCellColorExt(c), true
13961399 case Mode256Colors:
1397 x := tcell.Color(grayAdjustment256(grayLookup256_101[s.Val]) + 1)
1400 x := tcell.Color(grayAdjustment256(grayLookup256_101[s.Val]) + 1) + tcell.ColorValid
13981401 return MakeTCellColorExt(x), true
13991402 case Mode88Colors:
1400 x := tcell.Color(grayAdjustment88(grayLookup88_101[s.Val]) + 1)
1403 x := tcell.Color(grayAdjustment88(grayLookup88_101[s.Val]) + 1) + tcell.ColorValid
14011404 return MakeTCellColorExt(x), true
14021405 default:
14031406 panic(errors.WithStack(ColorModeMismatch{Color: s, Mode: mode}))
14071410 //======================================================================
14081411
14091412 // TCellColor is an IColor using tcell's color primitives. If you are not porting from urwid or translating
1410 // from urwid, this is the simplest approach to using color. Note that the underlying tcell.Color value is
1411 // stored offset by 2 from the value tcell would use to actually render a colored cell. Tcell represents
1412 // e.g. black by 0, maroon by 1, and so on - that means the default/empty/zero value for a tcell.Color object
1413 // is the color black. Gowid's layering approach means that the empty value for a color should mean "no color
1414 // preference" - so we want the zero value to mean that. A tcell.Color of -1 means "default color". So gowid
1415 // coopts -2 to mean "no color preference". We store the tcell.Color offset by 2 so the empty value for a
1416 // TCellColor means "no color preference". When we convert to a tcell.Color, we subtract 2 (but since a value
1417 // of -2 is meaningless to tcell, the caller should check and not pass on a value of -2 to tcell APIs - see
1418 // gowid.ColorNone)
1413 // from urwid, this is the simplest approach to using color. Gowid's layering approach means that the empty
1414 // value for a color should mean "no color preference" - so we want the zero value to mean that. A tcell.Color
1415 // of 0 means "default color". So gowid coopts nil to mean "no color preference".
14191416 type TCellColor struct {
1420 tc tcell.Color
1417 tc *tcell.Color
14211418 }
14221419
14231420 var (
14331430 match := tcellColorRE.FindStringSubmatch(val) // e.g. "Color00"
14341431 if len(match) == 2 {
14351432 n, _ := strconv.ParseUint(match[1], 16, 8)
1436 return MakeTCellColorExt(tcell.Color(n)), nil
1433 return MakeTCellColorExt(tcell.Color(n) + tcell.ColorValid), nil
14371434 } else if col, ok := tcell.ColorNames[val]; !ok {
14381435 return TCellColor{}, errors.WithStack(InvalidColor{Color: val})
14391436 } else {
14431440
14441441 // MakeTCellColor returns an initialized TCellColor given a tcell.Color input. The values that can be
14451442 // used are provided here: https://github.com/gdamore/tcell/blob/master/color.go#L41.
1446 func MakeTCellColorSafe(val tcell.Color) (TCellColor, error) {
1447 return TCellColor{val + 2}, nil
1448 }
1449
1450 // MakeTCellColor returns an initialized TCellColor given a tcell.Color input. The values that can be
1451 // used are provided here: https://github.com/gdamore/tcell/blob/master/color.go#L41.
14521443 func MakeTCellColorExt(val tcell.Color) TCellColor {
1453 res, _ := MakeTCellColorSafe(val)
1454 return res
1444 return TCellColor{&val}
14551445 }
14561446
14571447 // MakeTCellNoColor returns an initialized TCellColor that represents "no color" - meaning if another
14581448 // color is rendered "under" this one, then the color underneath will be displayed.
14591449 func MakeTCellNoColor() TCellColor {
1460 res := MakeTCellColorExt(-2)
1461 return res
1450 return TCellColor{}
14621451 }
14631452
14641453 // String implements Stringer for '%v' support.
14651454 func (r TCellColor) String() string {
1466 c := r.tc - 2
1467 if c == -2 {
1455 if r.tc == nil {
14681456 return "[no-color]"
14691457 } else {
1458 c := *r.tc
14701459 return fmt.Sprintf("TCellColor(%v)", tcell.Color(c))
14711460 }
14721461 }
14731462
14741463 // ToTCell converts a TCellColor back to a tcell.Color for passing to tcell APIs.
14751464 func (r TCellColor) ToTCell() tcell.Color {
1476 return r.tc - 2
1465 if r.tc == nil {
1466 return tcell.ColorDefault
1467 }
1468 return *r.tc
14771469 }
14781470
14791471 // ToTCellColor is a no-op, and exists so that TCellColor conforms to the IColor interface.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package gowid
55 import (
66 "testing"
77
8 "github.com/gdamore/tcell"
8 tcell "github.com/gdamore/tcell/v2"
99 "github.com/go-test/deep"
1010 "github.com/stretchr/testify/assert"
1111 )
1717 i2 := i2a.ToTCell()
1818 // See https://jonasjacek.github.io/colors/ - we are skipping
1919 // colors 0-21 inclusive
20 if i2 != 232 {
20 if i2 != tcell.Color232 {
2121 t.Errorf("Failed")
2222 }
2323 }
2727 c, _ := MakeRGBColorExtSafe(0, 0, 0)
2828 i2a, _ := c.ToTCellColor(Mode256Colors)
2929 i2 := i2a.ToTCell()
30 if i2 != 0 {
30 if i2 != tcell.ColorValid {
3131 t.Errorf("Failed")
3232 }
3333 }
3636 c := NewUrwidColor("dark red")
3737 i2a, _ := c.ToTCellColor(Mode256Colors)
3838 i2 := i2a.ToTCell()
39 if i2 != 2-1 {
39 if i2 != tcell.ColorMaroon {
4040 t.Errorf("Failed")
4141 }
4242 }
6060 if c.Val != 100 {
6161 t.Errorf("Failed")
6262 }
63 if v, _ := c.ToTCellColor(Mode256Colors); v.ToTCell() != 232 {
63 if v, _ := c.ToTCellColor(Mode256Colors); v.ToTCell() != tcell.Color232 {
6464 t.Errorf("Failed")
6565 }
6666 }
7070 if c.Val != 3 {
7171 t.Errorf("Failed")
7272 }
73 if v, _ := c.ToTCellColor(Mode256Colors); v.ToTCell() != 233 {
73 if v, _ := c.ToTCellColor(Mode256Colors); v.ToTCell() != tcell.Color233 {
7474 t.Errorf("Failed")
7575 }
7676 }
8080 if c.Val != 0 {
8181 t.Errorf("Failed")
8282 }
83 if v, _ := c.ToTCellColor(Mode256Colors); v.ToTCell() != 17 {
83 if v, _ := c.ToTCellColor(Mode256Colors); v.ToTCell() != tcell.Color17 {
8484 t.Errorf("Failed")
8585 }
8686 }
133133 func TestGray881(t *testing.T) {
134134 c := MakeGrayColor("g100")
135135 v, _ := c.ToTCellColor(Mode88Colors)
136 assert.Equal(t, v.ToTCell(), tcell.Color(80))
136 assert.Equal(t, v.ToTCell(), tcell.Color80)
137137 }
138138
139139 func TestDefault1(t *testing.T) {
140140 c, _ := MakeColorSafe("default")
141141 v, _ := c.ToTCellColor(Mode256Colors)
142 assert.Equal(t, v.ToTCell(), tcell.Color(-1))
142 assert.Equal(t, v.ToTCell(), tcell.ColorDefault)
143143 }
144144
145145 func TestTCell1(t *testing.T) {
149149 }
150150
151151 func TestTCell2(t *testing.T) {
152 c, _ := MakeTCellColorSafe(tcell.ColorMaroon)
152 c := MakeTCellColorExt(tcell.ColorMaroon)
153153 v, _ := c.ToTCellColor(Mode256Colors)
154154 assert.Equal(t, v.ToTCell(), tcell.ColorMaroon)
155155 }
218218 case tcell.KeyEnter:
219219 w.IWidget = text.New(fmt.Sprintf("Nice to meet you, %s.\n\nPress Q to exit.", w.IWidget.(*edit.Widget).Text()))
220220 default:
221 res = gowid.UserInput(w.IWidget, ev, size, focus, app)
221 res = w.IWidget.UserInput(w.IWidget, ev, size, focus, app)
222222 }
223223 }
224224 return res
266266 sbtn := styled.New(btn, gowid.MakeStyledAs(gowid.StyleReverse))
267267 div := divider.NewBlank()
268268
269 btn.OnClick(gowid.WidgetChangedCallback{"cb", func(app gowid.IApp, w gowid.IWidget) {
269 btn.OnClick(gowid.WidgetCallback{"cb", func(app gowid.IApp, w gowid.IWidget) {
270270 app.Quit()
271271 }})
272272
273 ask.OnTextSet(gowid.WidgetChangedCallback{"cb", func(app gowid.IApp, w gowid.IWidget) {
273 ask.OnTextSet(gowid.WidgetCallback{"cb", func(app gowid.IApp, w gowid.IWidget) {
274274 if ask.Text() == "" {
275275 reply.SetText("", app)
276276 } else {
294294 }
295295 ```
296296 - The bottom-most widget in the pile is a `button.Widget`. It itself wraps an inner widget, and when rendered will add characters on the left and right of the inner widget to create a button effect.
297 - `button.Widget` can call an interface method when it's clicked. `OnClick()` expects an `IWidgetChangedCallback`. You can use the `WidgetChangedCallback()` adapter to pass a simple function.
298 - The first parameter of `WidgetChangedCallback` is an `interface{}`. It's meant to uniquely identify this callback instance so that if you later need to remove the callback, you can by passing the same `interface{}`. Here I've used a simple string, "cb". The callbacks are scoped to the widget, so you can use the same callback identifier when registering callbacks for other widgets.
297 - `button.Widget` can call an interface method when it's clicked. `OnClick()` expects an `IWidgetChangedCallback`. You can use the `WidgetCallback()` adapter to pass a simple function.
298 - The first parameter of `WidgetCallback` is an `interface{}`. It's meant to uniquely identify this callback instance so that if you later need to remove the callback, you can by passing the same `interface{}`. Here I've used a simple string, "cb". The callbacks are scoped to the widget, so you can use the same callback identifier when registering callbacks for other widgets.
299299 - `edit.Widget` can call an interface method when its text changes. In this example, every time the user enters a character, `ask` will update the `reply` widget so that it displays a message.
300300 - The callback will be called with two arguments - the application `app` and the widget issuing the callback. But if it's more convenient, you can rely on Go's scope rules to capture the widgets that you need to modify in the callback. `ask`'s callback refers to `reply` and not the callback parameter `w`.
301301 - The `<exit>` button is styled using `MakeStyleAs()`, which applies a text style like underline, bold or reverse-video. No colors are given, so the button will use the terminal's default colors.
55
66 **Purpose:** The `asciigraph` widget renders line graphs. It uses the Go package `github.com/guptarohit/asciigraph`.
77
8 ![desc](https://drive.google.com/uc?export=view&id=19bDRxGYjtL00c1y6StrSZG63AM86Zm7C)
8 ![desc](https://user-images.githubusercontent.com/45680/118377433-35141900-b59b-11eb-818d-546dca83fd9e.png)
99
1010 **Examples:**
1111
3434
3535 **Purpose**: a clickable widget. The app can register callbacks to handle click events.
3636
37 ![desc](https://drive.google.com/uc?export=view&id=19kVB4t4c0dLwLusRaGj6oNrTWhNr02u2)
37 ![desc](https://user-images.githubusercontent.com/45680/118377515-b1a6f780-b59b-11eb-9003-1c37c008db39.png)
3838
3939 **Examples:**
4040
5656
5757 **Purpose**: a clickable widget with two states - selected and unselected.
5858
59 ![desc](https://drive.google.com/uc?export=view&id=1a7OBwLMzithJDwtylG1aLct0dLsyK2kn)
59 ![desc](https://user-images.githubusercontent.com/45680/118377546-e61ab380-b59b-11eb-8d6c-54b88608269a.png)
6060
6161 **Examples:**
6262
7474
7575 **Purpose**: arrange child widgets into vertical columns, with configurable column widths.
7676
77 ![desc](https://drive.google.com/uc?export=view&id=1kZI6n7wvO16PFu_-nTJ8t24WNsyasdU7)
77 ![desc](https://user-images.githubusercontent.com/45680/118377593-25490480-b59c-11eb-845b-51baf1936faf.png)
7878
7979 **Examples:**
8080 - `github.com/gcla/gowid/examples/gowid-widgets2`
8686
8787 **Purpose**: a modal dialog box that can be opened on top of another widget and will process the user input preferentially.
8888
89 ![desc](https://drive.google.com/uc?export=view&id=1gq_HJLXdnr0KJ2gPK4nalJ56WyPuVkJh)
89 ![desc](https://user-images.githubusercontent.com/45680/118377633-66411900-b59c-11eb-9c6f-74f7adb4c102.png)
9090
9191 **Examples:**
9292
9696
9797 **Purpose**: a configurable horizontal line that can be used to separate widgets arranged vertically. Can render using ascii or unicode.
9898
99 ![desc](https://drive.google.com/uc?export=view&id=1YRHeQvckXIVPwPf-8sLAWd3ZuT_qTfuL)
99 ![desc](https://user-images.githubusercontent.com/45680/118377670-b1f3c280-b59c-11eb-9e5a-10d5689f527f.png)
100100
101101 **Examples:**
102102
108108
109109 **Purpose**: a text area that will display text typed in by the user, with an optional caption/prefix.
110110
111 ![desc](https://drive.google.com/uc?export=view&id=1Wr_nFrRawGN_FyGv8pqBEegSrwboe3Fm)
111 ![desc](https://user-images.githubusercontent.com/45680/118377720-f8492180-b59c-11eb-918d-833fdd4a3586.png)
112112
113113 **Examples:**
114114
120120
121121 **Purpose**: a widget that when rendered returns a canvas full of the same user-supplied `Cell`.
122122
123 ![desc](https://drive.google.com/uc?export=view&id=1_O4P4YlikeP6j0vK8A7VPCXwxcnOh9a4)
123 ![desc](https://user-images.githubusercontent.com/45680/118377735-1f9fee80-b59d-11eb-8aef-2e6ea2b3fc6c.png)
124124
125125 **Examples:**
126126
140140
141141 **Purpose**: surround a child widget with a configurable "frame", using unicode or ascii characters.
142142
143 ![desc](https://drive.google.com/uc?export=view&id=1EUU4DZxPb6B4-u0XPN9rTgDxmHB9Z0lw)
143 ![desc](https://user-images.githubusercontent.com/45680/118377753-43633480-b59d-11eb-8fc2-4ca276376f26.png)
144144
145145 **Examples:**
146146
153153
154154 **Purpose**: a way to arrange widgets in a grid, with configurable horizontal alignment.
155155
156 ![desc](https://drive.google.com/uc?export=view&id=1ngHp3pzFzw7qSM8uQ-UmKiijIE4b_ULq)
156 ![desc](https://user-images.githubusercontent.com/45680/118377795-845b4900-b59d-11eb-82e5-b59e5b3d9619.png)
157157
158158 **Examples:**
159159
183183
184184 **Purpose**: a flexible widget to navigate a vertical list of widgets rendered in flow mode.
185185
186 ![desc](https://drive.google.com/uc?export=view&id=1uJ3Muv5zEu8HHK5DlBU9NEB_v9aHyemi)
186 ![desc](https://user-images.githubusercontent.com/45680/118377820-ad7bd980-b59d-11eb-8368-966567e626ff.png)
187187
188188 **Examples:**
189189
196196
197197 **Purpose**: a drop-down menu supporting arbitrarily many sub-menus.
198198
199 ![desc](https://drive.google.com/uc?export=view&id=1kLrAyPAbRi37VjxoVikSfjvrfxKiDVtQ)
199 ![desc](https://user-images.githubusercontent.com/45680/118377834-c4bac700-b59d-11eb-9884-fea03e543be8.png)
200200
201201 **Examples:**
202202
206206
207207 **Purpose**: a widget to render one widget over another, only passing user input to the occluded widget if the input coordinates are outside the boundaries of the widget on top.
208208
209 ![desc](https://drive.google.com/uc?export=view&id=1q8LcHhl-ZTA9AEIEWmgRGeRTnwjGyX4u)
209 ![desc](https://user-images.githubusercontent.com/45680/118377862-e2882c00-b59d-11eb-880b-5753239b92b0.png)
210210
211211 **Examples:**
212212
241241
242242 **Purpose**: arrange child widgets into horizontal bands, with configurable heights.
243243
244 ![desc](https://drive.google.com/uc?export=view&id=1Bnzhu-hHsr0Ok3hFP5Q0LlaQMTOPEepD)
244 ![desc](https://user-images.githubusercontent.com/45680/118377912-31ce5c80-b59e-11eb-84af-888729e98b25.png)
245245
246246 **Examples:**
247247
254254
255255 **Purpose**: a simple progress monitor.
256256
257 ![desc](https://drive.google.com/uc?export=view&id=15GK6PIlh_CswM6OQ0WNy2O6LC-EwMvhh)
257 ![desc](https://user-images.githubusercontent.com/45680/118377933-54f90c00-b59e-11eb-9589-200d829f2a80.png)
258258
259259 **Examples:**
260260
292292
293293 **Purpose**: a widget that, as part of a group, can be in a selected state (if no others in the group are selected) or is otherwise unselected.
294294
295 ![desc](https://drive.google.com/uc?export=view&id=1cJa4m-DaDpbGwUWChASh-m9AE0sI6tk3)
295 ![desc](https://user-images.githubusercontent.com/45680/118377974-8a055e80-b59e-11eb-834e-94080b1ff53b.png)
296296
297297 **Examples:**
298298
315315
316316 **Purpose**: adds a drop-shadow effect to a widget.
317317
318 ![desc](https://drive.google.com/uc?export=view&id=1BtI1f0nbyxDUhgsgYaHhOqV3sQJwZnoy)
318 ![desc](https://user-images.githubusercontent.com/45680/118377988-a1dce280-b59e-11eb-9fcd-1bfe57e7206b.png)
319319
320320 **Examples:**
321321
336336 ## table
337337
338338 **Purpose**: a widget to display tabular data in columns and rows.
339
340
341
342
339343
340344 ![desc](https://drive.google.com/uc?export=view&id=1TZXfT_VVf5g2sNYi9hia2h36krcGUBI8)
341345
395399
396400 **Purpose**: a VT-220 capable terminal emulator widget, heavily plagiarized from urwid's `vterm.py`.
397401
398 ![desc](https://drive.google.com/uc?export=view&id=1PNcMPwiybGBBu48Oot7_hm-uV55TJPTw)
402 ![desc](https://user-images.githubusercontent.com/45680/118378264-92f72f80-b5a0-11eb-99d0-0e51b4108870.png)
399403
400404 **Examples:**
401405
441445
442446 **Purpose**: a widget to render text, optionally styled and aligned.
443447
444 ![desc](https://drive.google.com/uc?export=view&id=1XJaSfqljC5ullPj5M9O2ld4OGtP9Nvji)
448 ![desc](https://user-images.githubusercontent.com/45680/118378052-fed89880-b59e-11eb-8605-1ce32217b755.png)
445449
446450 **Examples:**
447451
491495
492496 **Purpose**: a generalization of the `list` widget to render a tree structure.
493497
494 ![desc](https://drive.google.com/uc?export=view&id=1GDirTv-CeKH8CNBjSNrHG7WtE7ccU93P)
498 ![desc](https://user-images.githubusercontent.com/45680/118378280-b02bfe00-b5a0-11eb-92e7-4178f718f844.png)
495499
496500 **Examples:**
497501
512516
513517 **Purpose**: a vertical scroll bar with clickable arrows on either end.
514518
515 ![desc](https://drive.google.com/uc?export=view&id=1dUArLQ1KuzwQmthpTs-HXwxkHvTOVCuT)
519 ![desc](https://user-images.githubusercontent.com/45680/118378077-292a5600-b59f-11eb-8dbb-c31fdf08337d.png)
516520
517521 **Examples:**
518522
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // An example of the gowid asciigraph widget which relies upon the
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A simple gowid directory browser.
2020 "github.com/gcla/gowid/widgets/styled"
2121 "github.com/gcla/gowid/widgets/text"
2222 "github.com/gcla/gowid/widgets/tree"
23 "github.com/gdamore/tcell"
23 tcell "github.com/gdamore/tcell/v2"
2424 log "github.com/sirupsen/logrus"
2525 kingpin "gopkg.in/alecthomas/kingpin.v2"
2626 )
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A poor-man's editor using gowid widgets - shows dialog, edit and vscroll.
2323 "github.com/gcla/gowid/widgets/styled"
2424 "github.com/gcla/gowid/widgets/text"
2525 "github.com/gcla/gowid/widgets/vscroll"
26 "github.com/gdamore/tcell"
26 tcell "github.com/gdamore/tcell/v2"
2727 log "github.com/sirupsen/logrus"
2828 kingpin "gopkg.in/alecthomas/kingpin.v2"
2929 )
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A port of urwid's fib.py example using gowid widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A port of urwid's graph.py example using gowid widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A port of urwid's "Hello World" example from the tutorial, using gowid widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A demonstration of gowid's menu, list and overlay widgets.
1919 "github.com/gcla/gowid/widgets/styled"
2020 "github.com/gcla/gowid/widgets/text"
2121 "github.com/gcla/gowid/widgets/vpadding"
22 "github.com/gdamore/tcell"
22 tcell "github.com/gdamore/tcell/v2"
2323 log "github.com/sirupsen/logrus"
2424 )
2525
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A demonstration of gowid's overlay and fill widgets.
1010 "github.com/gcla/gowid/widgets/fill"
1111 "github.com/gcla/gowid/widgets/overlay"
1212 "github.com/gcla/gowid/widgets/styled"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 log "github.com/sirupsen/logrus"
1515 )
1616
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A demonstration of gowid's overlay, fill, asciigraph and radio widgets.
2121 "github.com/gcla/gowid/widgets/styled"
2222 "github.com/gcla/gowid/widgets/text"
2323 "github.com/gcla/gowid/widgets/vpadding"
24 "github.com/gdamore/tcell"
24 tcell "github.com/gdamore/tcell/v2"
2525 asc "github.com/guptarohit/asciigraph"
2626 log "github.com/sirupsen/logrus"
2727 )
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A demonstration of gowid's overlay and fill widgets.
1111 "github.com/gcla/gowid/widgets/overlay"
1212 "github.com/gcla/gowid/widgets/styled"
1313 "github.com/gcla/gowid/widgets/text"
14 "github.com/gdamore/tcell"
14 tcell "github.com/gdamore/tcell/v2"
1515 log "github.com/sirupsen/logrus"
1616 )
1717
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A port of urwid's palette_test.py example using gowid widgets.
1919 "github.com/gcla/gowid/widgets/radio"
2020 "github.com/gcla/gowid/widgets/styled"
2121 "github.com/gcla/gowid/widgets/text"
22 "github.com/gdamore/tcell"
22 tcell "github.com/gdamore/tcell/v2"
2323 log "github.com/sirupsen/logrus"
2424 )
2525
00 //go:generate statik -src=data
11
2 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
2 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
33 // code is governed by the MIT license that can be found in the LICENSE
44 // file.
55
1717 _ "github.com/gcla/gowid/examples/gowid-table/statik"
1818 kingpin "gopkg.in/alecthomas/kingpin.v2"
1919
20 "github.com/gdamore/tcell"
20 tcell "github.com/gdamore/tcell/v2"
2121 "github.com/rakyll/statik/fs"
2222 log "github.com/sirupsen/logrus"
2323
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A very poor-man's tmux written using gowid's terminal widget.
1919 "github.com/gcla/gowid/widgets/styled"
2020 "github.com/gcla/gowid/widgets/terminal"
2121 "github.com/gcla/gowid/widgets/text"
22 "github.com/gdamore/tcell"
22 tcell "github.com/gdamore/tcell/v2"
2323 log "github.com/sirupsen/logrus"
2424 )
2525
203203 defer f.Close()
204204
205205 palette := gowid.Palette{
206 "invred": gowid.MakePaletteEntry(gowid.ColorBlack, gowid.ColorRed),
207 "line": gowid.MakeStyledPaletteEntry(gowid.NewUrwidColor("black"), gowid.NewUrwidColor("light gray"), gowid.StyleBold),
206 "invred": gowid.MakePaletteEntry(gowid.ColorBlack, gowid.ColorRed),
207 "invblue": gowid.MakePaletteEntry(gowid.ColorBlack, gowid.ColorCyan),
208 "line": gowid.MakeStyledPaletteEntry(gowid.NewUrwidColor("black"), gowid.NewUrwidColor("light gray"), gowid.StyleBold),
208209 }
209210
210211 hkDuration := terminal.HotKeyDuration{time.Second * 3}
222223 }
223224
224225 for _, cmd := range tcommands {
225 app, err := terminal.NewExt(terminal.Options{
226 Command: strings.Split(cmd, " "),
227 HotKeyPersistence: &hkDuration,
228 Scrollback: 100,
226 tapp, err := terminal.NewExt(terminal.Options{
227 Command: strings.Split(cmd, " "),
228 HotKeyPersistence: &hkDuration,
229 Scrollback: 100,
230 Scrollbar: true,
231 EnableBracketedPaste: true,
232 HotKeyFns: []terminal.HotKeyInputFn{
233 func(ev *tcell.EventKey, w terminal.IWidget, app gowid.IApp) bool {
234 if w2, ok := w.(terminal.IScrollbar); ok {
235 if ev.Key() == tcell.KeyRune && ev.Rune() == 's' {
236 if w2.ScrollbarEnabled() {
237 w2.DisableScrollbar(app)
238 } else {
239 w2.EnableScrollbar(app)
240 }
241 return true
242 }
243 }
244 return false
245 },
246 },
229247 })
230248 if err != nil {
231249 panic(err)
232250 }
233 twidgets = append(twidgets, app)
251 twidgets = append(twidgets, tapp)
234252 }
235253
236254 tw := text.New(" Terminal Demo ")
237 twi := styled.New(tw, gowid.MakePaletteRef("invred"))
255 twir := styled.New(tw, gowid.MakePaletteRef("invred"))
256 twib := styled.New(tw, gowid.MakePaletteRef("invblue"))
238257 twp := holder.New(tw)
239258
240259 vline := styled.New(fill.New('│'), gowid.MakePaletteRef("line"))
265284 })
266285 t.OnBell(gowid.WidgetCallback{"cb",
267286 func(app gowid.IApp, w gowid.IWidget) {
268 twp.SetSubWidget(twi, app)
287 twp.SetSubWidget(twir, app)
269288 timer := time.NewTimer(time.Millisecond * 800)
270289 go func() {
271290 <-timer.C
281300 tw.SetText(" "+w2.GetTitle()+" ", app)
282301 },
283302 })
303 t.OnHotKey(gowid.WidgetCallback{"cb",
304 func(app gowid.IApp, w gowid.IWidget) {
305 w2 := w.(*terminal.Widget)
306 if w2.HotKeyActive() {
307 twp.SetSubWidget(twib, app)
308 } else {
309 twp.SetSubWidget(tw, app)
310 }
311 },
312 })
284313 }
285314
286315 app, err = gowid.NewApp(gowid.AppArgs{
287 View: view,
288 Palette: &palette,
289 Log: log.StandardLogger(),
316 View: view,
317 Palette: &palette,
318 Log: log.StandardLogger(),
319 EnableBracketedPaste: true,
290320 })
291321 examples.ExitOnErr(err)
292322
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
2020 "github.com/gcla/gowid/widgets/styled"
2121 "github.com/gcla/gowid/widgets/text"
2222 "github.com/gcla/gowid/widgets/tree"
23 "github.com/gdamore/tcell"
23 tcell "github.com/gdamore/tcell/v2"
2424 log "github.com/sirupsen/logrus"
2525 )
2626
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // The first example from the gowid tutorial.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // The second example from the gowid tutorial.
99 "github.com/gcla/gowid"
1010 "github.com/gcla/gowid/examples"
1111 "github.com/gcla/gowid/widgets/text"
12 "github.com/gdamore/tcell"
12 tcell "github.com/gdamore/tcell/v2"
1313 )
1414
1515 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // The third example from the gowid tutorial.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // The fourth example from the gowid tutorial.
1010 "github.com/gcla/gowid/examples"
1111 "github.com/gcla/gowid/widgets/edit"
1212 "github.com/gcla/gowid/widgets/text"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 )
1515
1616 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // The fifth example from the gowid tutorial.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // The sixth example from the gowid tutorial.
1212 "github.com/gcla/gowid/widgets/list"
1313 "github.com/gcla/gowid/widgets/pile"
1414 "github.com/gcla/gowid/widgets/text"
15 "github.com/gdamore/tcell"
15 tcell "github.com/gdamore/tcell/v2"
1616 )
1717
1818 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A gowid test app which exercises the pile, button, edit and progress widgets.
2020 "github.com/gcla/gowid/widgets/styled"
2121 "github.com/gcla/gowid/widgets/text"
2222 "github.com/gcla/gowid/widgets/vpadding"
23 "github.com/gdamore/tcell"
23 tcell "github.com/gdamore/tcell/v2"
2424 log "github.com/sirupsen/logrus"
2525 )
2626
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A gowid test app which exercises the columns, checkbox, edit and styled widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A gowid test app which exercises the button, grid, progress and radio widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A gowid test app which exercises the columns, list and framed widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A gowid test app which exercises the edit and vpadding widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A gowid test app which exercises the list, edit, columns and styled widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A gowid test app which exercises the list, edit and framed widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // A gowid test app which exercises the checkbox, columns and hpadding widgets.
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package examples
00 module github.com/gcla/gowid
1
2 go 1.13
13
24 require (
35 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect
46 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
5 github.com/gdamore/tcell v1.3.1-0.20200115030318-bff4943f9a29
7 github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e
8 github.com/creack/pty v1.1.15
9 github.com/gdamore/tcell/v2 v2.5.0
610 github.com/go-test/deep v1.0.1
711 github.com/guptarohit/asciigraph v0.4.1
812 github.com/hashicorp/golang-lru v0.5.1
913 github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
10 github.com/kr/pty v1.1.4
11 github.com/lucasb-eyer/go-colorful v1.0.3
12 github.com/mattn/go-runewidth v0.0.7
14 github.com/lucasb-eyer/go-colorful v1.2.0
15 github.com/mattn/go-runewidth v0.0.13
1316 github.com/pkg/errors v0.8.1
1417 github.com/rakyll/statik v0.1.6
1518 github.com/sirupsen/logrus v1.4.2
16 github.com/stretchr/testify v1.3.0
17 golang.org/x/text v0.3.2
19 github.com/stretchr/testify v1.7.0
20 golang.org/x/text v0.3.7
1821 gopkg.in/alecthomas/kingpin.v2 v2.2.6
1922 )
11 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
22 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
33 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
4 github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e h1:OjdSMCht0ZVX7IH0nTdf00xEustvbtUGRgMh3gbdmOg=
5 github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
6 github.com/creack/pty v1.1.15 h1:cKRCLMj3Ddm54bKSpemfQ8AtYFBhAI2MPmdys22fBdc=
7 github.com/creack/pty v1.1.15/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
48 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
59 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
610 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
711 github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
812 github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
9 github.com/gdamore/tcell v1.3.1-0.20200114053155-ef4e0a2e861f h1:9KnZ/v1noCLfM5yvKsq3TXwTCV8nH5Hwg0s/jrV/WS8=
10 github.com/gdamore/tcell v1.3.1-0.20200114053155-ef4e0a2e861f/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
11 github.com/gdamore/tcell v1.3.1-0.20200115030318-bff4943f9a29 h1:kvzEHvL4/ORuWe6JN6WeaiRYIvVDUVaC2r0gpJIxJ6I=
12 github.com/gdamore/tcell v1.3.1-0.20200115030318-bff4943f9a29/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
13 github.com/gdamore/tcell/v2 v2.5.0 h1:/LA5f/wqTP5mWT79czngibKVVx5wOgdFTIXPQ68fMO8=
14 github.com/gdamore/tcell/v2 v2.5.0/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo=
1315 github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
1416 github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
1517 github.com/guptarohit/asciigraph v0.4.1 h1:YHmCMN8VH81BIUIgTg2Fs3B52QDxNZw2RQ6j5pGoSxo=
1618 github.com/guptarohit/asciigraph v0.4.1/go.mod h1:9fYEfE5IGJGxlP1B+w8wHFy7sNZMhPtn59f0RLtpRFM=
1719 github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
1820 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
19 github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
2021 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
2122 github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
2223 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
23 github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
24 github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
25 github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
26 github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
27 github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
28 github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
24 github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
25 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
26 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
27 github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
28 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
2929 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
3030 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
3131 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3232 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3333 github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
3434 github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
35 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
36 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
37 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
38 github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
3539 github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
3640 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
3741 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
38 github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
3942 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
40 github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
4143 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
42 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
43 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
44 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
45 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
4446 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
45 golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
46 golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
47 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
47 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
48 golang.org/x/sys v0.0.0-20220318055525-2edf467146b5 h1:saXMvIOKvRFwbOMicHXr0B1uwoxq9dGmLe5ExMES6c4=
49 golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
50 golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
51 golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
4852 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
49 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
50 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
53 golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
54 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
5155 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
5256 gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
5357 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
58 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
59 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
60 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
61 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package gwtest
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
99 "testing"
1010
1111 "github.com/gcla/gowid"
12 "github.com/gdamore/tcell"
12 tcell "github.com/gdamore/tcell/v2"
1313 log "github.com/sirupsen/logrus"
1414 "github.com/stretchr/testify/assert"
1515 )
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // Package gwutil provides general-purpose utilities that are not used by
1818
1919 //======================================================================
2020
21 // Min returns the smaller of two integer arguments.
22 func Min(a, b int) int {
23 if a < b {
24 return a
25 }
26 return b
27 }
28
29 // Min returns the larger of two integer arguments.
30 func Max(a, b int) int {
31 if a > b {
32 return a
33 }
34 return b
21 // Min returns the smaller of >1 integer arguments.
22 func Min(i int, js ...int) int {
23 res := i
24 for _, j := range js {
25 if j < res {
26 res = j
27 }
28 }
29 return res
30 }
31
32 // Min returns the larger of >1 integer arguments.
33 func Max(i int, js ...int) int {
34 res := i
35 for _, j := range js {
36 if j > res {
37 res = j
38 }
39 }
40 return res
3541 }
3642
3743 // LimitTo is a one-liner that uses Min and Max to bound a value. Assumes
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package gwutil
4343 assert.Equal(t, "None", fmt.Sprintf("%v", opt1))
4444 }
4545
46 func TestMin1(t *testing.T) {
47 assert.Equal(t, 1, Min(1, 2, 3))
48 assert.Equal(t, 1, Min(2, 1, 3))
49 assert.Equal(t, 1, Min(2, 3, 1))
50 }
51
52 func TestMax1(t *testing.T) {
53 assert.Equal(t, 3, Max(1, 2, 3))
54 assert.Equal(t, 3, Max(2, 1, 3))
55 assert.Equal(t, 3, Max(2, 3, 1))
56 }
57
4658 //======================================================================
4759 // Local Variables:
4860 // mode: Go
0 // Copyright 2022 Graham Clark. All rights reserved. Use of this source
1 // code is governed by the MIT license that can be found in the LICENSE
2 // file.
3 //
4 // +build !windows
5
6 package gowid
7
8 import (
9 "os"
10
11 tcell "github.com/gdamore/tcell/v2"
12 )
13
14 //======================================================================
15
16 func tcellScreen(ttys string) (tcell.Screen, error) {
17 var tty tcell.Tty
18 var err error
19
20 tty, err = tcell.NewDevTtyFromDev(bestTty(ttys))
21 if err != nil {
22 return nil, WithKVs(err, map[string]interface{}{"tty": ttys})
23 }
24
25 return tcell.NewTerminfoScreenFromTty(tty)
26 }
27
28 func bestTty(tty string) string {
29 if tty != "" {
30 return tty
31 }
32 gwtty := os.Getenv("GOWID_TTY")
33 if gwtty != "" {
34 return gwtty
35 }
36 return "/dev/tty"
37 }
38
39 //======================================================================
40 // Local Variables:
41 // mode: Go
42 // fill-column: 110
43 // End:
0 // Copyright 2022 Graham Clark. All rights reserved. Use of this source
1 // code is governed by the MIT license that can be found in the LICENSE
2 // file.
3 //
4
5 package gowid
6
7 import (
8 tcell "github.com/gdamore/tcell/v2"
9 )
10
11 //======================================================================
12
13 func tcellScreen(tty string) (tcell.Screen, error) {
14 return tcell.NewScreen()
15 }
16
17 //======================================================================
18 // Local Variables:
19 // mode: Go
20 // fill-column: 110
21 // End:
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
99 "strings"
1010
1111 "github.com/gcla/gowid/gwutil"
12 "github.com/gdamore/tcell"
12 tcell "github.com/gdamore/tcell/v2"
1313 "github.com/pkg/errors"
1414 )
1515
12691269 case IRenderFixed:
12701270 subSize = RenderFixed{}
12711271 case IRenderFlowWith:
1272 subSize = RenderFlowWith{C: d2.FlowColumns()}
1272 subSize = RenderFlowWith{C: gwutil.Max(0, gwutil.Min(sz.BoxColumns(), d2.FlowColumns()))}
12731273 case IRenderFlow:
12741274 subSize = RenderFlowWith{C: sz.BoxColumns()}
12751275 case IRenderWithUnits:
1276 subSize = RenderBox{C: sz.BoxColumns(), R: d2.Units()}
1276 subSize = RenderBox{C: sz.BoxColumns(), R: gwutil.Max(0, gwutil.Min(sz.BoxRows(), d2.Units()))}
12771277 case IRenderRelative:
12781278 subSize = RenderBox{C: sz.BoxColumns(), R: int((d2.Relative() * float64(sz.BoxRows())) + 0.5)}
12791279 case IRenderWithWeight:
13471347 case IRenderFixed:
13481348 subSize = RenderFixed{}
13491349 case IRenderBox:
1350 subSize = RenderBox{C: w2.BoxColumns(), R: w2.BoxRows()}
1350 subSize = RenderBox{
1351 C: gwutil.Max(0, gwutil.Min(sz.BoxColumns(), w2.BoxColumns())),
1352 R: gwutil.Max(0, gwutil.Min(sz.BoxRows(), w2.BoxRows())),
1353 }
13511354 case IRenderFlowWith:
1352 subSize = RenderFlowWith{C: w2.FlowColumns()}
1355 subSize = RenderFlowWith{C: gwutil.Max(0, gwutil.Min(sz.BoxColumns(), w2.FlowColumns()))}
13531356 case IRenderFlow:
1354 subSize = RenderFlowWith{C: sz.BoxColumns()}
1357 subSize = RenderFlowWith{C: gwutil.Max(0, gwutil.Min(sz.BoxColumns(), sz.BoxColumns()))}
13551358 case IRenderRelative:
13561359 subSize = RenderBox{C: int((w2.Relative() * float64(sz.BoxColumns())) + 0.5), R: sz.BoxRows()}
13571360 case IRenderWithUnits:
1358 subSize = RenderBox{C: w2.Units(), R: sz.BoxRows()}
1361 subSize = RenderBox{
1362 C: gwutil.Max(0, gwutil.Min(sz.BoxColumns(), w2.Units())),
1363 R: sz.BoxRows(),
1364 }
13591365 default:
13601366 return nil, errors.WithStack(DimensionError{Size: size, Dim: d})
13611367 }
13641370 case IRenderFixed:
13651371 subSize = RenderFixed{}
13661372 case IRenderBox:
1367 subSize = RenderBox{C: w2.BoxColumns(), R: w2.BoxRows()}
1373 subSize = RenderBox{
1374 C: gwutil.Max(0, gwutil.Min(sz.FlowColumns(), w2.BoxColumns())),
1375 R: w2.BoxRows(),
1376 }
13681377 case IRenderFlowWith:
1369 subSize = RenderFlowWith{C: w2.FlowColumns()}
1378 subSize = RenderFlowWith{C: gwutil.Max(0, gwutil.Min(sz.FlowColumns(), w2.FlowColumns()))}
13701379 case IRenderFlow:
13711380 subSize = RenderFlowWith{C: sz.FlowColumns()}
13721381 case IRenderRelative:
13731382 subSize = RenderFlowWith{C: int((w2.Relative() * float64(sz.FlowColumns())) + 0.5)}
13741383 case IRenderWithUnits:
1375 subSize = RenderFlowWith{C: w2.Units()}
1384 subSize = RenderFlowWith{C: gwutil.Max(0, gwutil.Min(sz.FlowColumns(), w2.Units()))}
13761385 default:
13771386 return nil, errors.WithStack(DimensionError{Size: size, Dim: d})
13781387 }
13941403 // TODO - doc
13951404 func ComputeSubSize(size IRenderSize, w IWidgetDimension, h IWidgetDimension) (IRenderSize, error) {
13961405 var subSize IRenderSize
1406
1407 maxh := 1000000
1408 if mh, ok := h.(IRenderMaxUnits); ok {
1409 maxh = mh.MaxUnits()
1410 }
1411 maxw := 1000000
1412 if mw, ok := w.(IRenderMaxUnits); ok {
1413 maxw = mw.MaxUnits()
1414 }
13971415
13981416 switch sz := size.(type) {
13991417 case IRenderFixed:
14011419 case IRenderFixed:
14021420 subSize = RenderFixed{}
14031421 case IRenderBox:
1404 subSize = RenderBox{C: w2.BoxColumns(), R: w2.BoxRows()}
1422 subSize = RenderBox{
1423 C: gwutil.Min(maxw, w2.BoxColumns()),
1424 R: gwutil.Min(maxh, w2.BoxRows()),
1425 }
14051426 case IRenderFlowWith:
1406 subSize = RenderFlowWith{C: w2.FlowColumns()}
1427 subSize = RenderFlowWith{
1428 C: gwutil.Min(maxw, w2.FlowColumns()),
1429 }
14071430 case IRenderWithUnits:
14081431 switch h2 := h.(type) {
14091432 case IRenderWithUnits:
1410 subSize = RenderBox{C: w2.Units(), R: h2.Units()}
1433 subSize = RenderBox{
1434 C: gwutil.Min(maxw, w2.Units()),
1435 R: gwutil.Min(maxh, h2.Units()),
1436 }
14111437 default:
14121438 subSize = RenderFixed{}
14131439 }
14191445 case IRenderFixed:
14201446 subSize = RenderFixed{}
14211447 case IRenderBox:
1422 subSize = RenderBox{C: w2.BoxColumns(), R: w2.BoxRows()}
1448 subSize = RenderBox{
1449 C: gwutil.Max(0, gwutil.Min(maxw, sz.BoxColumns(), w2.BoxColumns())),
1450 R: gwutil.Max(0, gwutil.Min(maxh, sz.BoxRows(), w2.BoxRows())),
1451 }
14231452 case IRenderFlowWith:
1424 subSize = RenderFlowWith{C: w2.FlowColumns()}
1453 subSize = RenderFlowWith{
1454 C: gwutil.Max(0, gwutil.Min(maxw, sz.BoxColumns(), w2.FlowColumns())),
1455 }
14251456 case IRenderFlow:
1426 subSize = RenderFlowWith{C: sz.BoxColumns()}
1457 subSize = RenderFlowWith{
1458 C: gwutil.Max(0, gwutil.Min(maxw, sz.BoxColumns(), sz.BoxColumns())),
1459 }
14271460 case IRenderRelative:
14281461 cols := int((w2.Relative() * float64(sz.BoxColumns())) + 0.5)
14291462 switch h2 := h.(type) {
14301463 case IRenderRelative:
14311464 rows := int((h2.Relative() * float64(sz.BoxRows())) + 0.5)
1432 subSize = RenderBox{C: cols, R: rows}
1465 subSize = RenderBox{
1466 C: gwutil.Min(maxw, cols),
1467 R: gwutil.Min(maxh, rows),
1468 }
14331469 case IRenderWithUnits:
14341470 rows := h2.Units()
1435 subSize = RenderBox{C: cols, R: rows}
1471 subSize = RenderBox{
1472 C: gwutil.Min(maxw, cols),
1473 R: gwutil.Min(maxh, sz.BoxRows(), rows),
1474 }
14361475 case IRenderFlow:
1437 subSize = RenderFlowWith{C: cols}
1476 subSize = RenderFlowWith{
1477 C: gwutil.Min(maxw, cols),
1478 }
14381479 case IRenderFixed:
1439 subSize = RenderFlowWith{C: cols}
1480 subSize = RenderFlowWith{
1481 C: gwutil.Min(maxw, cols),
1482 }
14401483 default:
14411484 return nil, errors.WithStack(DimensionError{Size: size, Dim: w})
14421485 }
14431486 case IRenderWithUnits:
1444 cols := w2.Units()
1445 switch h2 := h.(type) {
1487 cols := gwutil.Min(sz.BoxColumns(), w2.Units())
1488 switch h := h.(type) {
14461489 case IRenderRelative:
1447 rows := int((h2.Relative() * float64(sz.BoxRows())) + 0.5)
1448 subSize = RenderBox{C: cols, R: rows}
1490 rows := int((h.Relative() * float64(sz.BoxRows())) + 0.5)
1491 subSize = RenderBox{
1492 C: gwutil.Min(maxw, cols),
1493 R: gwutil.Min(maxh, rows),
1494 }
14491495 case IRenderWithUnits:
1450 rows := h2.Units()
1451 subSize = RenderBox{C: cols, R: rows}
1496 rows := h.Units()
1497 subSize = RenderBox{
1498 C: gwutil.Min(maxw, cols),
1499 R: gwutil.Max(0, gwutil.Min(maxh, sz.BoxRows(), rows)),
1500 }
14521501 case IRenderFlow:
1453 subSize = RenderFlowWith{C: cols}
1502 subSize = RenderFlowWith{C: gwutil.Min(maxw, cols)}
14541503 case IRenderFixed:
1455 subSize = RenderFlowWith{C: cols}
1504 subSize = RenderFlowWith{C: gwutil.Min(maxw, cols)}
14561505 default:
14571506 return nil, errors.WithStack(DimensionError{Size: size, Dim: w})
14581507 }
14641513 case IRenderFixed:
14651514 subSize = RenderFixed{}
14661515 case IRenderBox:
1467 subSize = RenderBox{C: w2.BoxColumns(), R: w2.BoxRows()}
1516 subSize = RenderBox{
1517 C: gwutil.Min(maxw, w2.BoxColumns()),
1518 R: gwutil.Max(0, gwutil.Min(maxh, sz.FlowColumns(), w2.BoxRows())),
1519 }
14681520 case IRenderFlowWith:
1469 subSize = RenderFlowWith{C: w2.FlowColumns()}
1521 subSize = RenderFlowWith{C: gwutil.Min(maxw, w2.FlowColumns())}
14701522 case IRenderFlow:
1471 subSize = RenderFlowWith{C: sz.FlowColumns()}
1523 subSize = RenderFlowWith{C: gwutil.Min(maxw, sz.FlowColumns())}
14721524 case IRenderRelative:
1473 subSize = RenderFlowWith{C: int((w2.Relative() * float64(sz.FlowColumns())) + 0.5)}
1525 subSize = RenderFlowWith{
1526 C: gwutil.Min(maxw, int((w2.Relative()*float64(sz.FlowColumns()))+0.5)),
1527 }
14741528 case IRenderWithUnits:
1475 subSize = RenderFlowWith{C: w2.Units()}
1529 subSize = RenderFlowWith{
1530 C: gwutil.Max(0, gwutil.Min(maxw, sz.FlowColumns(), w2.Units())),
1531 }
14761532 default:
14771533 return nil, errors.WithStack(DimensionError{Size: size, Dim: w})
14781534 }
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package gowid
66 "fmt"
77 "strings"
88
9 "github.com/gdamore/tcell"
9 tcell "github.com/gdamore/tcell/v2"
1010 )
1111
1212 //======================================================================
1212 "strings"
1313
1414 "github.com/gcla/gowid"
15 "github.com/gdamore/tcell"
15 tcell "github.com/gdamore/tcell/v2"
1616 )
1717
1818 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
88 "strings"
99 "testing"
1010
11 "github.com/gdamore/tcell"
11 tcell "github.com/gdamore/tcell/v2"
1212 "github.com/stretchr/testify/assert"
1313 )
1414
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code
11 // is governed by the MIT license that can be found in the LICENSE file.
22
33 // Package boxadapter provides a widget that will allow a box widget to be used
88 "fmt"
99
1010 "github.com/gcla/gowid"
11 "github.com/gdamore/tcell"
11 tcell "github.com/gdamore/tcell/v2"
1212 )
1313
1414 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package boxadapter
1010 "github.com/gcla/gowid/gwtest"
1111 "github.com/gcla/gowid/widgets/edit"
1212 "github.com/gcla/gowid/widgets/list"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 "github.com/stretchr/testify/assert"
1515 )
1616
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
99
1010 "github.com/gcla/gowid"
1111 "github.com/gcla/gowid/gwutil"
12 "github.com/gdamore/tcell"
12 tcell "github.com/gdamore/tcell/v2"
1313 )
1414
1515 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package button
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package checkbox
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
99 "fmt"
1010
1111 "github.com/gcla/gowid"
12 "github.com/gdamore/tcell"
12 tcell "github.com/gdamore/tcell/v2"
1313 )
1414
1515 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1212 "github.com/gcla/gowid/gwutil"
1313 "github.com/gcla/gowid/vim"
1414 "github.com/gcla/gowid/widgets/fill"
15 "github.com/gdamore/tcell"
15 tcell "github.com/gdamore/tcell/v2"
1616 )
1717
1818 //======================================================================
3232 }
3333
3434 type Widget struct {
35 widgets []gowid.IContainerWidget
36 focus int // -1 means nothing selectable
37 prefCol int // caches the last set prefered col. Passes it on if widget hasn't changed focus
38 opt Options
35 widgets []gowid.IContainerWidget
36 focus int // -1 means nothing selectable
37 prefCol int // caches the last set prefered col. Passes it on if widget hasn't changed focus
38 widthHelper []bool // optimizations to save frequent array allocations during use
39 widthHelper2 []bool
40 opt Options
3941 *gowid.Callbacks
4042 gowid.AddressProvidesID
4143 gowid.SubWidgetsCallbacks
6668 opt.RightKeys = vim.AllRightKeys
6769 }
6870 res := &Widget{
69 widgets: widgets,
70 focus: -1,
71 prefCol: -1,
72 opt: opt,
71 widgets: widgets,
72 focus: -1,
73 prefCol: -1,
74 widthHelper: make([]bool, len(widgets)),
75 widthHelper2: make([]bool, len(widgets)),
76 opt: opt,
7377 }
7478 res.SubWidgetsCallbacks = gowid.SubWidgetsCallbacks{CB: &res.Callbacks}
7579 res.FocusCallbacks = gowid.FocusCallbacks{CB: &res.Callbacks}
158162 ws[i] = &gowid.ContainerWidget{IWidget: iw, D: gowid.RenderFlow{}}
159163 }
160164 }
165 w.widthHelper = make([]bool, len(widgets))
166 w.widthHelper2 = make([]bool, len(widgets))
161167 oldFocus := w.Focus()
162168 w.widgets = ws
163169 w.SetFocus(app, oldFocus)
269275 return vim.KeyIn(evk, w.opt.RightKeys)
270276 }
271277
278 type IWidthHelper interface {
279 WidthHelpers() ([]bool, []bool)
280 }
281
282 var _ IWidthHelper = (*Widget)(nil)
283
284 func (w *Widget) WidthHelpers() ([]bool, []bool) {
285 return w.widthHelper, w.widthHelper2
286 }
287
272288 //''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
273289
274290 func SubWidgetSize(size gowid.IRenderSize, newX int, dim gowid.IWidgetDimension) gowid.IRenderSize {
310326 }
311327
312328 func UserInput(w IWidget, ev interface{}, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
313 res := true
329 res := false
314330 subfocus := w.Focus()
315331
316332 subSizes := w.WidgetWidths(size, focus, subfocus, app)
321337 forChild := false
322338
323339 if subfocus != -1 {
340 res = true
324341 if evm, ok := ev.(*tcell.EventMouse); ok {
325342 curX := 0
326343 mx, _ := evm.Position()
417434 lenw := len(subs)
418435
419436 res := make([]int, lenw)
420 helper := make([]bool, lenw)
437 var widthHelper []bool
438 var widthHelper2 []bool
439 if w, ok := w.(IWidthHelper); ok {
440 // Save some allocations
441 widthHelper, widthHelper2 = w.WidthHelpers()
442 defer func() {
443 for i := 0; i < len(widthHelper); i++ {
444 widthHelper[i] = false
445 widthHelper2[i] = false
446 }
447 }()
448 } else {
449 widthHelper = make([]bool, lenw)
450 widthHelper2 = make([]bool, lenw)
451 }
421452
422453 haveColsTotal := false
423454 var colsTotal int
450481 res[i] = c.BoxColumns()
451482 trunc(&res[i])
452483 colsUsed += res[i]
453 helper[i] = true
484 widthHelper[i] = true
485 widthHelper2[i] = true
454486 case gowid.IRenderBox:
455487 res[i] = w2.BoxColumns()
456488 trunc(&res[i])
457489 colsUsed += res[i]
458 helper[i] = true
490 widthHelper[i] = true
491 widthHelper2[i] = true
459492 case gowid.IRenderFlowWith:
460493 res[i] = w2.FlowColumns()
461494 trunc(&res[i])
462495 colsUsed += res[i]
463 helper[i] = true
496 widthHelper[i] = true
497 widthHelper2[i] = true
464498 case gowid.IRenderWithUnits:
465499 res[i] = w2.Units()
466500 trunc(&res[i])
467501 colsUsed += res[i]
468 helper[i] = true
502 widthHelper[i] = true
503 widthHelper2[i] = true
469504 case gowid.IRenderRelative:
470505 cols, ok := size.(gowid.IColumns)
471506 if !ok {
474509 res[i] = int((w2.Relative() * float64(cols.Columns())) + 0.5)
475510 trunc(&res[i])
476511 colsUsed += res[i]
477 helper[i] = true
512 widthHelper[i] = true
513 widthHelper2[i] = true
478514 case gowid.IRenderWithWeight:
479515 // widget must be weighted
480516 totalWeight += w2.Weight()
481 helper[i] = false
517 widthHelper[i] = false
518 widthHelper2[i] = false
482519 default:
483520 panic(gowid.DimensionError{Size: size, Dim: w2})
484521 }
493530
494531 // Now, divide up the remaining space among the weight columns
495532 lasti := -1
533 maxedOut := false
496534 for {
497535 if colsLeft == 0 {
498536 break
500538 doneone := false
501539 totalWeight = 0
502540 for i := 0; i < lenw; i++ {
503 if w2, ok := dims[i].(gowid.IRenderWithWeight); ok && !helper[i] {
541 if w2, ok := dims[i].(gowid.IRenderWithWeight); ok && !widthHelper[i] {
504542 totalWeight += w2.Weight()
505543 }
506544 }
507545 colsToDivideUp = colsLeft
508546 for i := 0; i < lenw; i++ {
509547 // Can only be weight here if !helper[i] ; but not sufficient for it to be eligible
510 if !helper[i] {
548 if !widthHelper[i] {
511549 cols := int(((float32(dims[i].(gowid.IRenderWithWeight).Weight()) / float32(totalWeight)) * float32(colsToDivideUp)) + 0.5)
512 if max, ok := dims[i].(gowid.IRenderMaxUnits); ok {
513 if cols > max.MaxUnits() {
514 cols = max.MaxUnits()
515 helper[i] = true // this one is done
550 if !maxedOut {
551 if max, ok := dims[i].(gowid.IRenderMaxUnits); ok {
552 if cols >= max.MaxUnits() {
553 cols = max.MaxUnits()
554 widthHelper[i] = true // this one is done
555 }
516556 }
517557 }
518558 if cols > colsLeft {
524564 }
525565 res[i] += cols
526566 colsLeft -= cols
527 lasti = i
567 lasti = gwutil.Max(i, lasti)
528568 doneone = true
529569 }
530570 }
531571 }
532572 if !doneone {
533 break
573 // We used up all our extra space, after all weighted columns were maxed out. So
574 // we're done. Any extra space (should be just 1 unit at most) goes on the last columns.
575 if maxedOut {
576 break
577 }
578 // All the weighted columns have been assigned, and all were maxed out. We still
579 // have space to assign. So now grow the weighted columns, even though they don't need
580 // any more space for a full render - what else to do with the space!
581 maxedOut = true
582 // Reset; all false indices will be indices of weighted columns again
583 for i := 0; i < len(widthHelper); i++ {
584 widthHelper[i] = widthHelper2[i]
585 }
534586 }
535587 }
536588 if lasti != -1 && colsLeft > 0 {
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
88
99 "github.com/gcla/gowid"
1010 "github.com/gcla/gowid/gwtest"
11 "github.com/gcla/gowid/widgets/button"
1112 "github.com/gcla/gowid/widgets/checkbox"
1213 "github.com/gcla/gowid/widgets/fill"
1314 "github.com/gcla/gowid/widgets/selectable"
1415 "github.com/gcla/gowid/widgets/text"
15 "github.com/gdamore/tcell"
16 tcell "github.com/gdamore/tcell/v2"
1617 "github.com/stretchr/testify/assert"
1718 )
1819
151152 return renderWeightUpTo{gowid.RenderWithWeight{W: w}, max}
152153 }
153154
155 func weight(w int) gowid.RenderWithWeight {
156 return gowid.RenderWithWeight{W: w}
157 }
158
154159 func makep(c rune) gowid.IWidget {
155160 return selectable.New(fill.New(c))
156161 }
168173 w = New(subs)
169174 c = w.Render(gowid.RenderBox{C: 12, R: 1}, gowid.Focused, gwtest.D)
170175 assert.Equal(t, "xxxxxyyyyyzz", c.String())
176 }
177
178 func TestColumns5(t *testing.T) {
179 // None are selectable
180 subs := []gowid.IContainerWidget{
181 &gowid.ContainerWidget{text.New("x"), gowid.RenderWithWeight{W: 1}},
182 &gowid.ContainerWidget{text.New("y"), gowid.RenderWithWeight{W: 1}},
183 }
184 w := New(subs)
185 sz := gowid.RenderBox{C: 2, R: 1}
186 c := w.Render(sz, gowid.Focused, gwtest.D)
187 assert.Equal(t, "xy", c.String())
188
189 evright := gwtest.CursorRight()
190 acc := w.UserInput(evright, sz, gowid.Focused, gwtest.D)
191
192 // Nothing in here should accept the input, so it should bubble back up
193 assert.False(t, acc)
194 }
195
196 type renderWithUnitsMax struct {
197 gowid.RenderWithUnits
198 gowid.RenderMax
199 }
200
201 func TestColumns6(t *testing.T) {
202 h := renderWithUnitsMax{
203 RenderWithUnits: gowid.RenderWithUnits{1},
204 }
205
206 f := fill.New(' ')
207
208 subs := []gowid.IContainerWidget{
209 &gowid.ContainerWidget{f, h},
210 &gowid.ContainerWidget{button.NewBare(text.New("1")), gowid.RenderWithWeight{W: 4}},
211 &gowid.ContainerWidget{f, h},
212 &gowid.ContainerWidget{button.NewBare(text.New("0.000000")), gowid.RenderWithWeight{W: 8}},
213 &gowid.ContainerWidget{f, h},
214 &gowid.ContainerWidget{button.NewBare(text.New("192.168.44.123")), gowid.RenderWithWeight{W: 14}},
215 &gowid.ContainerWidget{f, h},
216 &gowid.ContainerWidget{button.NewBare(text.New("192.168.44.213")), gowid.RenderWithWeight{W: 14}},
217 &gowid.ContainerWidget{f, h},
218 &gowid.ContainerWidget{button.NewBare(text.New("TFTP")), gowid.RenderWithWeight{W: 6}},
219 &gowid.ContainerWidget{f, h},
220 &gowid.ContainerWidget{button.NewBare(text.New("77")), gowid.RenderWithWeight{W: 7}},
221 &gowid.ContainerWidget{f, h},
222 &gowid.ContainerWidget{button.NewBare(text.New("Read Request, File: C:\\IBMTCPIP\\lccm.1, Transfer type: octet")), gowid.RenderWithWeight{W: 60}},
223 &gowid.ContainerWidget{f, h},
224 }
225 w := New(subs)
226 sz := gowid.RenderFlowWith{C: 158}
227 c := w.Render(sz, gowid.Focused, gwtest.D)
228 assert.Equal(t, " 1 0.000000 192.168.44.123 192.168.44.213 TFTP 77 Read Request, File: C:\\IBMTCPIP\\lccm.1, Transfer type: octet ", c.String())
229
230 }
231
232 func TestColumns7(t *testing.T) {
233 d2 := weightupto(1, 2) // weight 1, max 2
234 d3 := weightupto(1, 3) // weight 1, max 3
235 d4 := weightupto(1, 4) // weight 1, max 4
236
237 sz4 := gowid.RenderFlowWith{C: 4}
238 sz6 := gowid.RenderFlowWith{C: 6}
239
240 subs := []gowid.IContainerWidget{
241 &gowid.ContainerWidget{text.New("aa"), d2},
242 &gowid.ContainerWidget{text.New("bb"), d2},
243 }
244
245 w := New(subs)
246 c := w.Render(sz4, gowid.Focused, gwtest.D)
247 assert.Equal(t, "aabb", c.String())
248
249 subs = []gowid.IContainerWidget{
250 &gowid.ContainerWidget{text.New("aa"), d3},
251 &gowid.ContainerWidget{text.New("bb"), d3},
252 }
253 w = New(subs)
254
255 c = w.Render(sz4, gowid.Focused, gwtest.D)
256 assert.Equal(t, "aabb", c.String())
257
258 c = w.Render(sz6, gowid.Focused, gwtest.D)
259 assert.Equal(t, "aa bb ", c.String())
260
261 subs = []gowid.IContainerWidget{
262 &gowid.ContainerWidget{text.New("aaaa"), d4},
263 &gowid.ContainerWidget{text.New("bb"), d2},
264 }
265 w = New(subs)
266
267 c = w.Render(sz6, gowid.Focused, gwtest.D)
268 assert.Equal(t, "aaaabb", c.String())
269
270 subs = []gowid.IContainerWidget{
271 &gowid.ContainerWidget{text.New("aaaa"), weight(1)},
272 &gowid.ContainerWidget{text.New("bb"), weight(1)},
273 }
274 w = New(subs)
275
276 c = w.Render(sz6, gowid.Focused, gwtest.D)
277 assert.Equal(t, "aaabb \na ", c.String())
171278 }
172279
173280 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1919 "github.com/gcla/gowid/widgets/shadow"
2020 "github.com/gcla/gowid/widgets/styled"
2121 "github.com/gcla/gowid/widgets/text"
22 "github.com/gdamore/tcell"
22 tcell "github.com/gdamore/tcell/v2"
2323 )
2424
2525 //======================================================================
4141 SetHeight(gowid.IWidgetDimension, gowid.IApp)
4242 }
4343
44 type ISwitchFocus interface {
45 IsSwitchFocus() bool
46 SwitchFocus(gowid.IApp)
47 }
48
49 type IModal interface {
50 IsModal() bool
51 }
52
4453 type IMaximizer interface {
4554 IsMaxed() bool
4655 Maximize(gowid.IApp)
5665 Options Options
5766 savedSubWidgetWidget gowid.IWidget
5867 savedContainer gowid.ISettableComposite
68 content *pile.Widget
5969 contentWrapper *gowid.ContainerWidget
6070 open bool
6171 maxer Maximizer
6676 var _ gowid.IWidget = (*Widget)(nil)
6777 var _ IWidget = (*Widget)(nil)
6878 var _ IMaximizer = (*Widget)(nil)
79 var _ ISwitchFocus = (*Widget)(nil)
80 var _ IModal = (*Widget)(nil)
6981
7082 type Options struct {
7183 Buttons []Button
7688 BorderStyle gowid.ICellStyler
7789 FocusOnWidget bool
7890 NoFrame bool
91 Modal bool
92 TabToButtons bool
93 StartIdx int
7994 }
8095
8196 type Button struct {
8297 Msg string
83 Action gowid.WidgetChangedFunction
98 Action gowid.IWidgetChangedCallback
8499 }
85100
86101 var Quit, Exit, CloseD, Cancel Button
89104 func init() {
90105 Quit = Button{
91106 Msg: "Quit",
92 Action: gowid.QuitFn,
107 Action: gowid.MakeWidgetCallback("quit", gowid.WidgetChangedFunction(gowid.QuitFn)),
93108 }
94109 Exit = Button{
95110 Msg: "Exit",
96 Action: gowid.QuitFn,
111 Action: gowid.MakeWidgetCallback("exit", gowid.WidgetChangedFunction(gowid.QuitFn)),
97112 }
98113 CloseD = Button{
99114 Msg: "Close",
104119 OkCancel = []Button{
105120 Button{
106121 Msg: "Ok",
107 Action: gowid.QuitFn,
122 Action: gowid.MakeWidgetCallback("okcancel", gowid.WidgetChangedFunction(gowid.QuitFn)),
108123 },
109124 Cancel,
110125 }
141156 buttonStyle, backgroundStyle, borderStyle := opt.ButtonStyle, opt.BackgroundStyle, opt.BorderStyle
142157
143158 if buttonStyle == nil {
144 buttonStyle = gowid.MakePaletteEntry(DefaultButtonText, DefaultButton)
159 buttonStyle = gowid.MakeStyledPaletteEntry(DefaultButtonText, DefaultButton, gowid.StyleNone)
145160 }
146161 if backgroundStyle == nil {
147 backgroundStyle = gowid.MakePaletteEntry(DefaultText, DefaultBackground)
162 backgroundStyle = gowid.MakeStyledPaletteEntry(DefaultText, DefaultBackground, gowid.StyleNone)
148163 }
149164 if borderStyle == nil {
150 borderStyle = gowid.MakePaletteEntry(DefaultButton, DefaultBackground)
165 borderStyle = gowid.MakeStyledPaletteEntry(DefaultButton, DefaultBackground, gowid.StyleNone)
151166 }
152167
153168 colsW := make([]gowid.IContainerWidget, 0)
165180 res.Close(app)
166181 }})
167182 } else {
168 bw.OnClick(gowid.WidgetCallback{fmt.Sprintf("cb-%d", i), b.Action})
183 bw.OnClick(b.Action)
169184 }
170185 colsW = append(colsW,
171186 &gowid.ContainerWidget{
181196 }
182197
183198 if len(colsW) > 0 {
184 cols := columns.New(colsW)
199 cols := columns.New(colsW, columns.Options{
200 StartColumn: opt.StartIdx * 2,
201 })
185202 pileW = append(pileW,
186203 styled.New(
187204 divider.NewUnicodeAlt(),
192209 }
193210
194211 dialogContent := pile.NewFlow(pileW...)
195 if !opt.FocusOnWidget {
196 dialogContent.SetFocus(nil, len(pileW)-1)
197 }
198212
199213 var d gowid.IWidget = dialogContent
200214 if !opt.NoFrame {
219233 res = &Widget{
220234 IWidget: d,
221235 contentWrapper: wrapper,
236 content: dialogContent,
222237 Options: opt,
223238 Callbacks: gowid.NewCallbacks(),
239 }
240
241 if !opt.FocusOnWidget {
242 res.FocusOnButtons(nil)
224243 }
225244
226245 return res
249268 return !w.Options.NoEscapeClose
250269 }
251270
271 func (w *Widget) IsModal() bool {
272 return w.Options.Modal
273 }
274
275 func (w *Widget) SwitchFocus(app gowid.IApp) {
276 f := w.content.Focus()
277 if f == 0 {
278 w.FocusOnButtons(app)
279 } else {
280 w.FocusOnContent(app)
281 }
282 }
283
284 func (w *Widget) IsSwitchFocus() bool {
285 return w.Options.TabToButtons
286 }
287
252288 func (w *Widget) IsOpen() bool {
253289 return w.open
254290 }
336372
337373 func (w *Widget) SetContentWidth(d gowid.IWidgetDimension, app gowid.IApp) {
338374 w.contentWrapper.D = d
375 }
376
377 func (w *Widget) FocusOnButtons(app gowid.IApp) {
378 w.content.SetFocus(app, len(w.content.SubWidgets())-1)
379 }
380
381 func (w *Widget) FocusOnContent(app gowid.IApp) {
382 w.content.SetFocus(app, 0)
339383 }
340384
341385 //''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
360404 func OpenExt(w IOpenExt, container gowid.ISettableComposite, width gowid.IWidgetDimension, height gowid.IWidgetDimension, app gowid.IApp) {
361405 ov := overlay.New(w, container.SubWidget(),
362406 gowid.VAlignMiddle{}, height, // Intended to mean use as much vertical space as you need
363 gowid.HAlignMiddle{}, width)
407 gowid.HAlignMiddle{}, width, overlay.Options{
408 IgnoreLowerStyle: true,
409 })
364410
365411 if _, ok := width.(gowid.IRenderFixed); ok {
366412 w.SetContentWidth(gowid.RenderFixed{}, app) // fixed or weight:1, ratio:0.5
376422 func UserInput(w IWidget, ev interface{}, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
377423 var res bool
378424 if w.IsOpen() {
379 handled := false
380425 if evk, ok := ev.(*tcell.EventKey); ok {
381426 switch {
382427 case evk.Key() == tcell.KeyCtrlC || evk.Key() == tcell.KeyEsc:
383428 if w.EscapeCloses() {
384429 w.GetNoFunction().Changed(app, w)
385 handled = true
430 res = true
386431 }
387432 }
388433 }
389 if !handled {
390 handled = gowid.UserInputIfSelectable(w.SubWidget(), ev, size, focus, app)
434 if !res {
435 res = gowid.UserInputIfSelectable(w.SubWidget(), ev, size, focus, app)
391436 }
392 if !handled {
437 if !res {
393438 if evk, ok := ev.(*tcell.EventKey); ok {
394439 switch {
395440 case evk.Key() == tcell.KeyRune && evk.Rune() == 'z':
399444 } else {
400445 w.Maximize(app)
401446 }
402 handled = true
447 res = true
448 }
449 case evk.Key() == tcell.KeyTab:
450 if w, ok := w.(ISwitchFocus); ok {
451 w.SwitchFocus(app)
452 res = true
403453 }
404454 }
455
405456 }
406457 }
407 res = true
458 if w, ok := w.(IModal); ok {
459 if w.IsModal() {
460 res = true
461 }
462 }
408463 } else {
409464 res = gowid.UserInputIfSelectable(w.SubWidget(), ev, size, focus, app)
410465 }
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package divider
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1313 "github.com/gcla/gowid"
1414 "github.com/gcla/gowid/gwutil"
1515 "github.com/gcla/gowid/widgets/text"
16 "github.com/gdamore/tcell"
16 tcell "github.com/gdamore/tcell/v2"
1717 "github.com/pkg/errors"
1818 )
1919
3434 MaskChr() rune
3535 }
3636
37 type IPaste interface {
38 PasteState(...bool) bool
39 AddKey(*tcell.EventKey)
40 GetKeys() []*tcell.EventKey
41 }
42
3743 type Mask struct {
3844 Chr rune
3945 Enable bool
6975 DownLines(size gowid.IRenderSize, doPage bool, app gowid.IApp) bool
7076 }
7177
78 type IReadOnly interface {
79 IsReadOnly() bool
80 }
81
7282 type Widget struct {
7383 IMask
7484 caption string
7585 text string
86 paste bool
87 readonly bool
88 pastedKeys []*tcell.EventKey
7689 cursorPos int
7790 linesFromTop int
7891 Callbacks *gowid.Callbacks
8295 var _ fmt.Stringer = (*Widget)(nil)
8396 var _ io.Reader = (*Widget)(nil)
8497 var _ gowid.IWidget = (*Widget)(nil)
98 var _ IPaste = (*Widget)(nil)
99 var _ IReadOnly = (*Widget)(nil)
85100
86101 // Writer embeds an EditWidget and provides the io.Writer interface. An gowid.IApp needs to
87102 // be provided too because the widget's SetText() function requires it in order to issue
92107 }
93108
94109 type Options struct {
95 Caption string
96 Text string
97 Mask IMask
110 Caption string
111 Text string
112 Mask IMask
113 ReadOnly bool
98114 }
99115
100116 func New(args ...Options) *Widget {
109125 IMask: opt.Mask,
110126 caption: opt.Caption,
111127 text: opt.Text,
128 readonly: opt.ReadOnly,
112129 cursorPos: len(opt.Text),
130 pastedKeys: make([]*tcell.EventKey, 0, 100),
113131 linesFromTop: 0,
114132 Callbacks: gowid.NewCallbacks(),
115133 }
118136
119137 func (w *Widget) String() string {
120138 return fmt.Sprintf("edit")
139 }
140
141 func (w *Widget) IsReadOnly() bool {
142 return w.readonly
143 }
144
145 func (w *Widget) SetReadOnly(v bool, _ gowid.IApp) {
146 w.readonly = v
121147 }
122148
123149 // Set content from array
187213 func (w *Widget) SetCaption(text string, app gowid.IApp) {
188214 w.caption = text
189215 gowid.RunWidgetCallbacks(w.Callbacks, Caption{}, app, w)
216 }
217
218 //func (w *Widget) PasteState(b ...bool) []*tcell.EventKey {
219 func (w *Widget) PasteState(b ...bool) bool {
220 if len(b) > 0 {
221 w.paste = b[0]
222 //if w.paste {
223 w.pastedKeys = w.pastedKeys[:0]
224 //}
225 }
226 return w.paste
227 }
228
229 func (w *Widget) AddKey(ev *tcell.EventKey) {
230 w.pastedKeys = append(w.pastedKeys, ev)
231 }
232
233 func (w *Widget) GetKeys() []*tcell.EventKey {
234 return w.pastedKeys
190235 }
191236
192237 func (w *Widget) CursorEnabled() bool {
381426 }
382427 }
383428
429 func keyIsPasteable(ev *tcell.EventKey) bool {
430 switch ev.Key() {
431 case tcell.KeyEnter, tcell.Key(' '), tcell.KeyRune:
432 return true
433 default:
434 return false
435 }
436 }
437
438 func isReadOnly(w interface{}) bool {
439 readOnly := false
440 if ro, ok := w.(IReadOnly); ok {
441 readOnly = ro.IsReadOnly()
442 }
443 return readOnly
444 }
445
446 func pasteableKeyInput(w IWidget, ev *tcell.EventKey, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
447 if isReadOnly(w) {
448 return false
449 }
450
451 handled := true
452 switch ev.Key() {
453 case tcell.KeyEnter:
454 r := []rune(w.Text())
455 w.SetText(string(r[0:w.CursorPos()])+string('\n')+string(r[w.CursorPos():]), app)
456 w.SetCursorPos(w.CursorPos()+1, app)
457 case tcell.Key(' '):
458 r := []rune(w.Text())
459 w.SetText(string(r[0:w.CursorPos()])+" "+string(r[w.CursorPos():]), app)
460 w.SetCursorPos(w.CursorPos()+1, app)
461 case tcell.KeyRune:
462 // TODO: this is lame. Inserting a character is O(n) where n is length
463 // of text. I should switch this to use the two stack model for edited
464 // text.
465 txt := w.Text()
466 r := []rune(txt)
467 cpos := w.CursorPos()
468 rhs := make([]rune, len(r)-cpos)
469 copy(rhs, r[cpos:])
470 w.SetText(string(append(append(r[:cpos], ev.Rune()), rhs...)), app)
471 w.SetCursorPos(w.CursorPos()+1, app)
472
473 default:
474 handled = false
475 }
476
477 return handled
478 }
479
384480 func UserInput(w IWidget, ev interface{}, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
385481 handled := true
386482 doup := false
387483 dodown := false
388484 recalcLinesFromTop := false
485 readOnly := isReadOnly(w)
486
389487 switch ev := ev.(type) {
390488 case *tcell.EventMouse:
391489 switch ev.Buttons() {
412510 handled = false
413511 }
414512
513 case *tcell.EventPaste:
514 if wp, ok := w.(IPaste); ok {
515 if ev.Start() {
516 wp.PasteState(true)
517 } else {
518 evs := wp.GetKeys()
519 wp.PasteState(false)
520 for _, ev := range evs {
521 pasteableKeyInput(w, ev, size, focus, app)
522 }
523 }
524 }
525
415526 case *tcell.EventKey:
416 switch ev.Key() {
417 case tcell.KeyPgUp:
418 handled = w.UpLines(size, true, app)
419 case tcell.KeyUp, tcell.KeyCtrlP:
420 doup = true
421 case tcell.KeyDown, tcell.KeyCtrlN:
422 dodown = true
423 case tcell.KeyPgDn:
424 handled = w.DownLines(size, true, app)
425 case tcell.KeyLeft, tcell.KeyCtrlB:
426 if w.CursorPos() > 0 {
427 w.SetCursorPos(w.CursorPos()-1, app)
428 } else {
527 handled = false
528 if wp, ok := w.(IPaste); ok {
529 if wp.PasteState() && keyIsPasteable(ev) && !readOnly {
530 wp.AddKey(ev)
531 handled = true
532 }
533 }
534
535 if !handled {
536 handled = pasteableKeyInput(w, ev, size, focus, app)
537 }
538
539 if !handled {
540 handled = true
541 switch ev.Key() {
542 case tcell.KeyPgUp:
543 handled = w.UpLines(size, true, app)
544 case tcell.KeyUp, tcell.KeyCtrlP:
545 doup = true
546 case tcell.KeyDown, tcell.KeyCtrlN:
547 dodown = true
548 case tcell.KeyPgDn:
549 handled = w.DownLines(size, true, app)
550 case tcell.KeyLeft, tcell.KeyCtrlB:
551 if w.CursorPos() > 0 {
552 w.SetCursorPos(w.CursorPos()-1, app)
553 } else {
554 handled = false
555 }
556 case tcell.KeyRight, tcell.KeyCtrlF:
557 if w.CursorPos() < utf8.RuneCountInString(w.Text()) {
558 w.SetCursorPos(w.CursorPos()+1, app)
559 } else {
560 handled = false
561 }
562 case tcell.KeyBackspace, tcell.KeyBackspace2:
563 if !readOnly {
564 if w.CursorPos() > 0 {
565 pos := w.CursorPos()
566 w.SetCursorPos(w.CursorPos()-1, app)
567 r := []rune(w.Text())
568 w.SetText(string(r[0:pos-1])+string(r[pos:]), app)
569 }
570 }
571 case tcell.KeyDelete, tcell.KeyCtrlD:
572 if !readOnly {
573 if w.CursorPos() < utf8.RuneCountInString(w.Text()) {
574 r := []rune(w.Text())
575 w.SetText(string(r[0:w.CursorPos()])+string(r[w.CursorPos()+1:]), app)
576 }
577 }
578 case tcell.KeyCtrlK:
579 if !readOnly {
580 r := []rune(w.Text())
581 w.SetText(string(r[0:w.CursorPos()]), app)
582 }
583 case tcell.KeyCtrlU:
584 if !readOnly {
585 r := []rune(w.Text())
586 w.SetText(string(r[w.CursorPos():]), app)
587 w.SetCursorPos(0, app)
588 }
589 case tcell.KeyHome:
590 w.SetCursorPos(0, app)
591 w.SetLinesFromTop(0, app)
592 case tcell.KeyCtrlW:
593 if !readOnly {
594 txt := []rune(w.Text())
595 origcp := w.CursorPos()
596 cp := origcp
597 for cp > 0 && unicode.IsSpace(txt[cp-1]) {
598 cp--
599 }
600 for cp > 0 && !unicode.IsSpace(txt[cp-1]) {
601 cp--
602 }
603 if cp != origcp {
604 w.SetText(string(txt[0:cp])+string(txt[origcp:]), app)
605 w.SetCursorPos(cp, app)
606 }
607 }
608 case tcell.KeyCtrlA:
609 // Would be nice to use a slice here, something that doesn't copy
610 // TODO: terrible O(n) behavior :-(
611 txt := w.Text()
612
613 i := w.CursorPos()
614 j := 0
615 lastnl := false
616 curstart := 0
617
618 for _, ch := range txt {
619 if lastnl {
620 curstart = j
621 }
622 lastnl = (ch == '\n')
623
624 if i == j {
625 break
626 }
627 j += 1
628 }
629
630 w.SetCursorPos(curstart, app)
631 recalcLinesFromTop = true
632
633 case tcell.KeyEnd:
634 w.SetCursorPos(utf8.RuneCountInString(w.Text()), app)
635 recalcLinesFromTop = true
636
637 case tcell.KeyCtrlE:
638 // TODO: terrible O(n) behavior :-(
639 txt := w.Text()
640 i := w.CursorPos()
641 j := 0
642 checknl := false
643 for _, ch := range txt {
644 if i == j {
645 checknl = true
646 }
647 j += 1
648 if checknl {
649 if ch == '\n' {
650 break
651 }
652 i += 1
653 }
654 }
655 w.SetCursorPos(i, app)
656 recalcLinesFromTop = true
657
658 default:
429659 handled = false
430660 }
431 case tcell.KeyRight, tcell.KeyCtrlF:
432 if w.CursorPos() < utf8.RuneCountInString(w.Text()) {
433 w.SetCursorPos(w.CursorPos()+1, app)
434 } else {
435 handled = false
436 }
437 case tcell.KeyBackspace, tcell.KeyBackspace2:
438 if w.CursorPos() > 0 {
439 pos := w.CursorPos()
440 w.SetCursorPos(w.CursorPos()-1, app)
441 r := []rune(w.Text())
442 w.SetText(string(r[0:pos-1])+string(r[pos:]), app)
443 }
444 case tcell.KeyDelete, tcell.KeyCtrlD:
445 if w.CursorPos() < utf8.RuneCountInString(w.Text()) {
446 r := []rune(w.Text())
447 w.SetText(string(r[0:w.CursorPos()])+string(r[w.CursorPos()+1:]), app)
448 }
449 case tcell.KeyEnter:
450 r := []rune(w.Text())
451 w.SetText(string(r[0:w.CursorPos()])+string('\n')+string(r[w.CursorPos():]), app)
452 w.SetCursorPos(w.CursorPos()+1, app)
453 case tcell.Key(' '):
454 r := []rune(w.Text())
455 w.SetText(string(r[0:w.CursorPos()])+" "+string(r[w.CursorPos():]), app)
456 w.SetCursorPos(w.CursorPos()+1, app)
457 case tcell.KeyCtrlK:
458 r := []rune(w.Text())
459 w.SetText(string(r[0:w.CursorPos()]), app)
460 case tcell.KeyCtrlU:
461 r := []rune(w.Text())
462 w.SetText(string(r[w.CursorPos():]), app)
463 w.SetCursorPos(0, app)
464 case tcell.KeyHome:
465 w.SetCursorPos(0, app)
466 w.SetLinesFromTop(0, app)
467 case tcell.KeyCtrlW:
468 txt := []rune(w.Text())
469 origcp := w.CursorPos()
470 cp := origcp
471 for cp > 0 && unicode.IsSpace(txt[cp-1]) {
472 cp--
473 }
474 for cp > 0 && !unicode.IsSpace(txt[cp-1]) {
475 cp--
476 }
477 if cp != origcp {
478 w.SetText(string(txt[0:cp])+string(txt[origcp:]), app)
479 w.SetCursorPos(cp, app)
480 }
481 case tcell.KeyCtrlA:
482 // Would be nice to use a slice here, something that doesn't copy
483 // TODO: terrible O(n) behavior :-(
484 txt := w.Text()
485
486 i := w.CursorPos()
487 j := 0
488 lastnl := false
489 curstart := 0
490
491 for _, ch := range txt {
492 if lastnl {
493 curstart = j
494 }
495 lastnl = (ch == '\n')
496
497 if i == j {
498 break
499 }
500 j += 1
501 }
502
503 w.SetCursorPos(curstart, app)
504 recalcLinesFromTop = true
505
506 case tcell.KeyEnd:
507 w.SetCursorPos(utf8.RuneCountInString(w.Text()), app)
508 recalcLinesFromTop = true
509
510 case tcell.KeyCtrlE:
511 // TODO: terrible O(n) behavior :-(
512 txt := w.Text()
513 i := w.CursorPos()
514 j := 0
515 checknl := false
516 for _, ch := range txt {
517 if i == j {
518 checknl = true
519 }
520 j += 1
521 if checknl {
522 if ch == '\n' {
523 break
524 }
525 i += 1
526 }
527 }
528 w.SetCursorPos(i, app)
529 recalcLinesFromTop = true
530
531 case tcell.KeyRune:
532 // TODO: this is lame. Inserting a character is O(n) where n is length
533 // of text. I should switch this to use the two stack model for edited
534 // text.
535 txt := w.Text()
536 r := []rune(txt)
537 cpos := w.CursorPos()
538 rhs := make([]rune, len(r)-cpos)
539 copy(rhs, r[cpos:])
540 w.SetText(string(append(append(r[:cpos], ev.Rune()), rhs...)), app)
541 w.SetCursorPos(w.CursorPos()+1, app)
542
543 default:
544 handled = false
545661 }
546662 }
547663
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package edit
1010
1111 "github.com/gcla/gowid"
1212 "github.com/gcla/gowid/gwtest"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 "github.com/stretchr/testify/assert"
1515 )
1616
2929 assert.Equal(t, "hi: 现在 abc ", c1.String())
3030
3131 evq := tcell.NewEventKey(tcell.KeyRune, 'q', tcell.ModNone)
32 evdel := tcell.NewEventKey(tcell.KeyDelete, 'X', tcell.ModNone)
3233
3334 w.SetCursorPos(0, gwtest.D)
3435 w.UserInput(evq, sz, gowid.Focused, gwtest.D)
3940 w.UserInput(evq, sz, gowid.Focused, gwtest.D)
4041 c1 = w.Render(sz, gowid.Focused, gwtest.D)
4142 assert.Equal(t, "qhi: 现q在 abc ", c1.String())
43 w.UserInput(evq, sz, gowid.Focused, gwtest.D)
44 c1 = w.Render(sz, gowid.Focused, gwtest.D)
45 assert.Equal(t, "qhi: 现qq在 abc", c1.String())
46 w.UserInput(evdel, sz, gowid.Focused, gwtest.D)
47 c1 = w.Render(sz, gowid.Focused, gwtest.D)
48 assert.Equal(t, "qhi: 现qq abc ", c1.String())
49
50 w.SetReadOnly(true, gwtest.D)
51 w.UserInput(evq, sz, gowid.Focused, gwtest.D)
52 c1 = w.Render(sz, gowid.Focused, gwtest.D)
53 assert.Equal(t, "qhi: 现qq abc ", c1.String())
54 w.UserInput(evdel, sz, gowid.Focused, gwtest.D)
55 c1 = w.Render(sz, gowid.Focused, gwtest.D)
56 assert.Equal(t, "qhi: 现qq abc ", c1.String())
57
58 w.SetReadOnly(false, gwtest.D)
59 w.UserInput(evq, sz, gowid.Focused, gwtest.D)
60 c1 = w.Render(sz, gowid.Focused, gwtest.D)
61 assert.Equal(t, "qhi: 现qqq abc ", c1.String())
4262 }
4363
4464 func TestRender1(t *testing.T) {
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // Package fixedadaptor provides a widget that will render a fixed widget when
88 "fmt"
99
1010 "github.com/gcla/gowid"
11 "github.com/gdamore/tcell"
11 tcell "github.com/gdamore/tcell/v2"
1212 )
1313
1414 //======================================================================
1515
1616 // Wraps a Fixed widget and turns it into a Box widget. If rendered in a Fixed
17 // context, render as normal. If rendered in a Box context, render as a Flow
17 // context, render as normal. If rendered in a Box context, render as a Fixed
1818 // widget, then either truncate or grow the resulting canvas to meet the
1919 // box size requirement.
2020 //
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package fixedadapter
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1111 "github.com/gcla/gowid"
1212 "github.com/gcla/gowid/gwutil"
1313 "github.com/gcla/gowid/widgets/text"
14 "github.com/gdamore/tcell"
14 tcell "github.com/gdamore/tcell/v2"
1515 "github.com/mattn/go-runewidth"
1616 )
1717
2828 UnicodeAltFrame = FrameRunes{'▛', '▜', '▙', '▟', '▀', '▄', '▌', '▐'}
2929 UnicodeAlt2Frame = FrameRunes{'╔', '╗', '╚', '╝', '═', '═', '║', '║'}
3030 SpaceFrame = FrameRunes{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}
31 nullFrame = FrameRunes{}
3132 )
3233
3334 func init() {
7475 }
7576 } else {
7677 opt = opts[0]
78 }
79
80 // Likely means nil value was used for 8-field struct - so this is a heuristic
81 if opt.Frame == nullFrame {
82 opt.Frame = AsciiFrame
7783 }
7884
7985 res := &Widget{
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package framed
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1717 "github.com/gcla/gowid/widgets/pile"
1818 "github.com/gcla/gowid/widgets/text"
1919 "github.com/gcla/gowid/widgets/vpadding"
20 "github.com/gdamore/tcell"
20 tcell "github.com/gdamore/tcell/v2"
2121 )
2222
2323 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1010 "github.com/gcla/gowid/gwtest"
1111 "github.com/gcla/gowid/widgets/button"
1212 "github.com/gcla/gowid/widgets/text"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 "github.com/stretchr/testify/assert"
1515 )
1616
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
99
1010 "github.com/gcla/gowid"
1111 "github.com/gcla/gowid/gwutil"
12 "github.com/gdamore/tcell"
12 tcell "github.com/gdamore/tcell/v2"
1313 )
1414
1515 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1212 "github.com/gcla/gowid/widgets/checkbox"
1313 "github.com/gcla/gowid/widgets/fill"
1414 "github.com/gcla/gowid/widgets/text"
15 "github.com/gdamore/tcell"
15 tcell "github.com/gdamore/tcell/v2"
1616 log "github.com/sirupsen/logrus"
1717 "github.com/stretchr/testify/assert"
1818 )
112112 assert.Equal(t, c.String(), "abcde ")
113113
114114 c = w5.Render(gowid.RenderFlowWith{C: 3}, gowid.Focused, gwtest.D)
115 assert.Equal(t, c.String(), "abc")
115 assert.Equal(t, c.String(), "abc\nde ")
116116
117117 c = w5.Render(gowid.RenderBox{C: 6, R: 1}, gowid.Focused, gwtest.D)
118118 assert.Equal(t, c.String(), "abcde ")
119119
120120 c = w5.Render(gowid.RenderBox{C: 3, R: 3}, gowid.Focused, gwtest.D)
121 assert.Equal(t, c.String(), "abc\n \n ")
121 assert.Equal(t, c.String(), "abc\nde \n ")
122122
123123 c = w5.Render(gowid.RenderBox{C: 7, R: 3}, gowid.Focused, gwtest.D)
124124 assert.Equal(t, c.String(), "abcde \n \n ")
222222
223223 w10 := New(fill.New('x'), gowid.HAlignLeft{Margin: 1}, gowid.RenderWithUnits{U: 6})
224224 c10 := w10.Render(gowid.RenderBox{C: 5, R: 2}, gowid.Focused, gwtest.D)
225 assert.Equal(t, c10.String(), "xxxxx\nxxxxx")
225 assert.Equal(t, c10.String(), " xxxx\n xxxx")
226226
227227 w11 := New(fill.New('x'), gowid.HAlignLeft{Margin: 1}, gowid.RenderWithUnits{U: 4})
228228 c11 := w11.Render(gowid.RenderBox{C: 5, R: 2}, gowid.Focused, gwtest.D)
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
55 package isselected
66
77 import (
8 "fmt"
9
810 "github.com/gcla/gowid"
911 )
1012
6365 return w.Not.Selectable()
6466 }
6567
68 func (w *Widget) String() string {
69 return fmt.Sprintf("issel[%v#%v#%v]", w.Not, w.Selected, w.Focused)
70 }
71
6672 //======================================================================
6773
6874 // For uses that require IComposite
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
88 "fmt"
99
1010 "github.com/gcla/gowid"
11 "github.com/gdamore/tcell"
11 tcell "github.com/gdamore/tcell/v2"
1212 )
1313
1414 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package keypress
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1010 "github.com/gcla/gowid"
1111 "github.com/gcla/gowid/gwutil"
1212 "github.com/gcla/gowid/vim"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 "github.com/pkg/errors"
1515 )
1616
212212
213213 type Options struct {
214214 //SelectedStyle gowid.ICellStyler // apply a style to the selected widget - orthogonal to focus styling
215 DownKeys []vim.KeyPress
216 UpKeys []vim.KeyPress
215 DownKeys []vim.KeyPress
216 UpKeys []vim.KeyPress
217 DoNotSetSelected bool // Whether or not to set the focus.Selected field for the selected child
217218 }
218219
219220 type IndexedWidget struct {
338339
339340 func (w *Widget) CalculateOnScreen(size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) (int, int, int, error) {
340341 return CalculateOnScreen(w, size, focus, app)
342 }
343
344 func (w *Widget) SelectChild(f gowid.Selector) bool {
345 return !w.options.DoNotSetSelected && f.Selected
341346 }
342347
343348 //''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
384389 //foobar := styled.New(curWidget, gowid.MakeStyledAs(gowid.StyleReverse))
385390 var curToRender gowid.IWidget = curWidget
386391 if haveCols {
387 //c = gowid.Render(curWidget, gowid.RenderFlowWith{C: cols.Columns()}, focus, app)
388 c = curToRender.Render(gowid.RenderFlowWith{C: cols.Columns()}, focus, app)
392 c = curToRender.Render(gowid.RenderFlowWith{C: cols.Columns()}, focus.SelectIf(w.SelectChild(focus)), app)
389393 } else {
390 //c = gowid.Render(curWidget, gowid.RenderFixed{}, focus, app)
391 c = curToRender.Render(gowid.RenderFixed{}, focus, app)
394 c = curToRender.Render(gowid.RenderFixed{}, focus.SelectIf(w.SelectChild(focus)), app)
392395 }
393396 creallines := c.BoxRows()
394397 middle = SubRenders{curWidget, curPos, c, creallines}
808811 break
809812 }
810813 if dirMoved > 0 {
811 _, curPosition = w.MoveToNextFocus(subRenderSize, focus, numLinesToUse, app)
814 res, curPosition = w.MoveToNextFocus(subRenderSize, focus, numLinesToUse, app)
812815 } else if dirMoved < 0 {
813 _, curPosition = w.MoveToPreviousFocus(subRenderSize, focus, numLinesToUse, app)
816 res, curPosition = w.MoveToPreviousFocus(subRenderSize, focus, numLinesToUse, app)
814817 } else {
815818 panic(BadState)
819 }
820 if !res {
821 w.st = saveState
822 w.Walker().SetFocus(startPosition, app)
823 break
816824 }
817825 }
818826 //res = true
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package list
1616 "github.com/gcla/gowid/widgets/pile"
1717 "github.com/gcla/gowid/widgets/selectable"
1818 "github.com/gcla/gowid/widgets/text"
19 "github.com/gdamore/tcell"
19 tcell "github.com/gdamore/tcell/v2"
2020 log "github.com/sirupsen/logrus"
2121 "github.com/stretchr/testify/assert"
2222 )
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1111 "github.com/gcla/gowid/widgets/holder"
1212 "github.com/gcla/gowid/widgets/null"
1313 "github.com/gcla/gowid/widgets/overlay"
14 "github.com/gdamore/tcell"
14 tcell "github.com/gdamore/tcell/v2"
1515 )
1616
1717 //======================================================================
3131 Name() string
3232 }
3333
34 type IOpener interface {
35 OpenMenu(*Widget, ISite, gowid.IApp) bool
36 CloseMenu(*Widget, gowid.IApp)
37 }
38
3439 type Options struct {
3540 CloseKeysProvided bool
3641 CloseKeys []gowid.IKey
3843 IgnoreKeys []gowid.IKey
3944 NoAutoClose bool
4045 Modal bool
46 OpenCloser IOpener
4147 }
4248
4349 var (
8591 opt = opts[0]
8692 }
8793
94 if opt.OpenCloser == nil {
95 opt.OpenCloser = OpenerFunc(OpenSimple)
96 }
97
8898 res := &Widget{
8999 name: name,
90100 width: width,
127137 return res
128138 }
129139
140 func (w *Widget) String() string {
141 return fmt.Sprintf("menu[%v]", w.Name())
142 }
143
130144 func (w *Widget) AutoClose() bool {
131145 return w.autoClose
132146 }
143157 w.overlay.SetWidth(width, app)
144158 }
145159
160 func (w *Widget) Height() gowid.IWidgetDimension {
161 return w.overlay.Height()
162 }
163
164 func (w *Widget) SetHeight(width gowid.IWidgetDimension, app gowid.IApp) {
165 w.overlay.SetHeight(width, app)
166 }
167
146168 func (w *Widget) Name() string {
147169 return w.name
148170 }
149171
150172 func (w *Widget) Open(site ISite, app gowid.IApp) {
173 w.opts.OpenCloser.OpenMenu(w, site, app)
174 }
175
176 func (w *Widget) OpenImpl(site ISite, app gowid.IApp) {
151177 w.site = site
152178 site.SetNamer(w, app)
153179 w.overlay.SetTop(w.top, app)
157183 }
158184
159185 func (w *Widget) Close(app gowid.IApp) {
186 w.opts.OpenCloser.CloseMenu(w, app)
187 }
188
189 func (w *Widget) CloseImpl(app gowid.IApp) {
160190 // protect against case where it's closed already
161191 if w.site != nil {
162192 w.site.SetNamer(nil, app)
170200 return w.overlay
171201 }
172202
173 func (w *Widget) String() string {
174 return "menu" // TODO: should iterate over submenus
175 }
176
177203 func (w *Widget) SetSubWidget(widget gowid.IWidget, app gowid.IApp) {
178204 w.baseHolder.IWidget = widget
179205 }
224250
225251 func (w *rejectKeyInput) UserInput(ev interface{}, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
226252 if _, ok := ev.(*tcell.EventKey); ok && w.on {
253 return false
254 }
255 if _, ok := ev.(*tcell.EventPaste); ok && w.on {
227256 return false
228257 }
229258 return w.IWidget.UserInput(ev, size, focus, app)
413442 }
414443
415444 //======================================================================
445
446 // Return false if it was already open
447 type OpenerFunc func(bool, *Widget, ISite, gowid.IApp) bool
448
449 func (m OpenerFunc) OpenMenu(mu *Widget, site ISite, app gowid.IApp) bool {
450 return m(true, mu, site, app)
451 }
452
453 func (m OpenerFunc) CloseMenu(mu *Widget, app gowid.IApp) {
454 m(false, mu, nil, app)
455 }
456
457 func OpenSimple(open bool, mu *Widget, site ISite, app gowid.IApp) bool {
458 if open {
459 mu.OpenImpl(site, app)
460 return true
461 } else {
462 mu.CloseImpl(app)
463 return true
464 }
465 }
466
467 //======================================================================
416468 // Local Variables:
417469 // mode: Go
418470 // fill-column: 110
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
99
1010 "github.com/gcla/gowid"
1111 "github.com/gcla/gowid/widgets/padding"
12 "github.com/gdamore/tcell"
12 tcell "github.com/gdamore/tcell/v2"
1313 )
1414
1515 //======================================================================
6767 BottomGetsFocus() bool
6868 TopGetsFocus() bool
6969 BottomGetsCursor() bool
70 }
71
72 type IIgnoreLowerStyle interface {
73 IgnoreLowerStyle() bool
7074 }
7175
7276 type IWidget interface {
99103 Callbacks *gowid.Callbacks
100104 }
101105
106 var _ IIgnoreLowerStyle = (*Widget)(nil)
107
102108 // For callback registration
103109 type Top struct{}
104110 type Bottom struct{}
107113 BottomGetsFocus bool
108114 TopGetsNoFocus bool
109115 BottomGetsCursor bool
116 IgnoreLowerStyle bool
110117 }
111118
112119 func New(top, bottom gowid.IWidget,
234241 }
235242 }
236243
244 func (w *Widget) IgnoreLowerStyle() bool {
245 return w.opts.IgnoreLowerStyle
246 }
247
237248 //======================================================================
238249
239250 func UserInput(w IOverlay, ev interface{}, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
252263 if !res {
253264 _, ok1 := ev.(*tcell.EventKey)
254265 _, ok2 := ev.(*tcell.EventMouse)
255 if notOccluded && (ok1 || ok2) {
266 _, ok3 := ev.(*tcell.EventPaste)
267 if notOccluded && (ok1 || ok2 || ok3) {
256268 res = gowid.UserInputIfSelectable(w.Bottom(), ev, size, focus, app)
257269 }
258270 }
259271 }
260272 return res
273 }
274
275 // Merge cells as follows - use upper rune if set, use upper colors if set,
276 // and use upper style only (don't let any lower run style bleed through)
277 func mergeAllExceptUpperStyle(lower gowid.Cell, upper gowid.Cell) gowid.Cell {
278 res := lower
279 if upper.HasRune() {
280 res = res.WithRune(upper.Rune())
281 }
282
283 ufg, ubg, _ := upper.GetDisplayAttrs()
284 if ubg != gowid.ColorNone {
285 res = res.WithBackgroundColor(ubg)
286 }
287 if ufg != gowid.ColorNone {
288 res = res.WithForegroundColor(ufg)
289 }
290
291 res = res.WithStyle(upper.Style())
292 return res
293 }
294
295 type iMergeWithFuncCanvas interface {
296 MergeWithFunc(gowid.IMergeCanvas, int, int, gowid.CellMergeFunc, bool)
261297 }
262298
263299 func Render(w IOverlay, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.ICanvas {
271307 bottomC2 := bottomC.Duplicate()
272308 p2 := padding.New(w.Top(), w.VAlign(), w.Height(), w.HAlign(), w.Width())
273309 topC := p2.Render(size, tfocus, app)
274 bottomC2.MergeUnder(topC, 0, 0, w.BottomGetsCursor())
310
311 var bottomC2mc iMergeWithFuncCanvas
312 ign := false
313 if wIgn, ok := w.(IIgnoreLowerStyle); ok {
314 if gc, ok := bottomC2.(iMergeWithFuncCanvas); ok {
315 bottomC2mc = gc
316 ign = wIgn.IgnoreLowerStyle()
317 }
318 }
319
320 if ign {
321 bottomC2mc.MergeWithFunc(topC, 0, 0, mergeAllExceptUpperStyle, w.BottomGetsCursor())
322 } else {
323 bottomC2.MergeUnder(topC, 0, 0, w.BottomGetsCursor())
324 }
325
275326 return bottomC2
276327 }
277328 }
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
1 // code is governed by the MIT license that can be found in the LICENSE
2 // file.
3
4 package overlay
5
6 import (
7 "testing"
8
9 "github.com/gcla/gowid"
10 "github.com/gcla/gowid/gwtest"
11 "github.com/gcla/gowid/widgets/styled"
12 "github.com/gcla/gowid/widgets/text"
13 "github.com/gdamore/tcell/v2"
14 "github.com/stretchr/testify/assert"
15 )
16
17 func TestOverlay1(t *testing.T) {
18 tw := text.New("top")
19 bw := text.New("bottom")
20 ov := New(tw, bw, gowid.VAlignTop{}, gowid.RenderFixed{}, gowid.HAlignLeft{}, gowid.RenderFixed{})
21 c := ov.Render(gowid.RenderFlowWith{C: 6}, gowid.Focused, gwtest.D)
22 assert.Equal(t, "toptom", c.String())
23
24 bwStyled := styled.New(bw, gowid.MakeStyledAs(gowid.StyleBold))
25
26 // When the widget is created this way, the style from the lower widget bleeds through
27 ov = New(tw, bwStyled, gowid.VAlignTop{}, gowid.RenderFixed{}, gowid.HAlignLeft{}, gowid.RenderFixed{})
28 c = ov.Render(gowid.RenderFlowWith{C: 6}, gowid.Focused, gwtest.D)
29 assert.Equal(t, "toptom", c.String())
30 assert.Equal(t, tcell.AttrBold, c.CellAt(0, 0).Style().OnOff&tcell.AttrBold)
31
32 // When the widget is created this way, the style from the upper widget is set unilaterally
33 ov = New(tw, bwStyled, gowid.VAlignTop{}, gowid.RenderFixed{}, gowid.HAlignLeft{}, gowid.RenderFixed{},
34 Options{
35 IgnoreLowerStyle: true,
36 })
37 c = ov.Render(gowid.RenderFlowWith{C: 6}, gowid.Focused, gwtest.D)
38 assert.Equal(t, "toptom", c.String())
39 assert.Equal(t, tcell.AttrMask(0), c.CellAt(0, 0).Style().OnOff&tcell.AttrBold)
40 }
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1111 "github.com/gcla/gowid"
1212 "github.com/gcla/gowid/gwutil"
1313 "github.com/gcla/gowid/widgets/fill"
14 "github.com/gdamore/tcell"
14 tcell "github.com/gdamore/tcell/v2"
1515 )
1616
1717 //======================================================================
149149 default:
150150 }
151151
152 switch al := w.VAlign().(type) {
153 case gowid.VAlignTop:
154 switch s := size2.(type) {
155 case gowid.IRenderBox:
156 size2 = gowid.RenderBox{C: s.BoxColumns(), R: s.BoxRows() - al.Margin}
157 }
158 }
159
152160 return gowid.ComputeSubSizeUnsafe(size2, w.Width(), w.Height())
153 //return SubWidgetSize(w, size, focus, app)
154161 }
155162
156163 func (w *Widget) RenderSize(size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.IRenderBox {
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package padding
1111 "github.com/gcla/gowid/widgets/fill"
1212 "github.com/gcla/gowid/widgets/framed"
1313 "github.com/gcla/gowid/widgets/text"
14 "github.com/gdamore/tcell"
14 tcell "github.com/gdamore/tcell/v2"
1515 "github.com/stretchr/testify/assert"
1616 )
17
18 type renderRatioUpTo struct {
19 gowid.RenderWithRatio
20 max int
21 }
22
23 func (s renderRatioUpTo) MaxUnits() int {
24 return s.max
25 }
26
27 func ratioupto(w float64, max int) renderRatioUpTo {
28 return renderRatioUpTo{gowid.RenderWithRatio{R: w}, max}
29 }
1730
1831 func TestPadding1(t *testing.T) {
1932 var w gowid.IWidget
2235 w = New(fill.New('x'), gowid.VAlignMiddle{}, gowid.RenderWithUnits{U: 2}, gowid.HAlignMiddle{}, gowid.RenderWithUnits{U: 2})
2336 c = w.Render(gowid.RenderBox{C: 4, R: 4}, gowid.Focused, gwtest.D)
2437 assert.Equal(t, " \n xx \n xx \n ", c.String())
38
39 w = New(fill.New('x'), gowid.VAlignMiddle{}, gowid.RenderWithUnits{U: 2}, gowid.HAlignMiddle{}, gowid.RenderWithRatio{R: 0.5})
40 c = w.Render(gowid.RenderBox{C: 4, R: 4}, gowid.Focused, gwtest.D)
41 assert.Equal(t, " \n xx \n xx \n ", c.String())
42
43 w = New(fill.New('x'), gowid.VAlignMiddle{}, ratioupto(0.5, 1), gowid.HAlignMiddle{}, gowid.RenderWithUnits{U: 2})
44 c = w.Render(gowid.RenderBox{C: 4, R: 4}, gowid.Focused, gwtest.D)
45 assert.Equal(t, " \n xx \n \n ", c.String())
46
47 w = New(fill.New('x'), gowid.VAlignMiddle{}, gowid.RenderWithUnits{U: 2}, gowid.HAlignMiddle{}, ratioupto(0.5, 1))
48 c = w.Render(gowid.RenderBox{C: 4, R: 4}, gowid.Focused, gwtest.D)
49 assert.Equal(t, " \n x \n x \n ", c.String())
2550
2651 w = New(text.New("foo"), gowid.VAlignMiddle{}, gowid.RenderFixed{}, gowid.HAlignMiddle{}, gowid.RenderFixed{})
2752 c = w.Render(gowid.RenderBox{C: 5, R: 3}, gowid.Focused, gwtest.D)
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2021 Graham Clark. All rights reserved. Use of this source
1 // code is governed by the MIT license that can be found in the LICENSE
2 // file.
3
4 // Package paragraph provides a simple text widget that neatly splits
5 // over multiple lines.
6 package paragraph
7
8 import (
9 "strings"
10
11 "github.com/gcla/gowid"
12 "github.com/gcla/gowid/gwutil"
13 )
14
15 //======================================================================
16
17 // Widget can be used to display text on the screen, with words broken
18 // cleanly as the output width changes.
19 type Widget struct {
20 words []string
21 lastcols int
22 lastrows int
23 gowid.RejectUserInput
24 gowid.NotSelectable
25 }
26
27 var _ gowid.IWidget = (*Widget)(nil)
28
29 func New(text string) *Widget {
30 return &Widget{
31 words: strings.Fields(text),
32 }
33 }
34
35 func NewWithWords(words ...string) *Widget {
36 return &Widget{
37 words: words,
38 }
39 }
40
41 func (w *Widget) Render(size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.ICanvas {
42 switch size := size.(type) {
43 case gowid.IRenderFlowWith:
44 res := gowid.NewCanvas()
45 var lc gowid.ICanvas
46 var pos int
47 var curWord string
48 i := 0
49 space := false
50 for {
51 if i >= len(w.words) {
52 if lc != nil {
53 res.AppendBelow(lc, false, false)
54 }
55 break
56 }
57 if curWord == "" {
58 curWord = w.words[i]
59 }
60 if lc == nil {
61 lc = gowid.NewCanvasOfSize(size.FlowColumns(), 1)
62 pos = 0
63 }
64 if space {
65 if pos < size.FlowColumns()-1 {
66 pos++
67 } else {
68 if lc != nil {
69 res.AppendBelow(lc, false, false)
70 lc = gowid.NewCanvasOfSize(size.FlowColumns(), 1)
71 pos = 0
72 }
73 }
74 space = false
75 } else {
76 // No space left in current line
77 if len(curWord) > size.FlowColumns()-pos {
78 // No space on this line, but it will fit on next line. If it
79 // doesn't even fit on a line by itself, well just split it
80 if pos > 0 && lc != nil && len(curWord) <= size.FlowColumns() {
81 res.AppendBelow(lc, false, false)
82 lc = gowid.NewCanvasOfSize(size.FlowColumns(), 1)
83 pos = 0
84 }
85 take := gwutil.Min(size.FlowColumns()-pos, len(curWord))
86 for j := 0; j < take; j++ {
87 lc.SetCellAt(pos, 0, gowid.CellFromRune(rune(curWord[j])))
88 pos++
89 }
90 if pos >= size.FlowColumns() {
91 res.AppendBelow(lc, false, false)
92 lc = nil
93 }
94 if take == len(curWord) {
95 i++
96 curWord = ""
97 space = true
98 } else {
99 // Must be less
100 curWord = curWord[take:]
101 }
102 } else {
103 for j := 0; j < len(curWord); j++ {
104 lc.SetCellAt(pos, 0, gowid.CellFromRune(rune(curWord[j])))
105 pos++
106 }
107 i++
108 curWord = ""
109 space = true
110 }
111 }
112
113 }
114 return res
115 default:
116 panic(gowid.WidgetSizeError{Widget: w, Size: size, Required: "gowid.IRenderFlow"})
117 }
118 }
119
120 func (w *Widget) RenderSize(size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.IRenderBox {
121 if w.lastcols != 0 {
122 return gowid.RenderBox{C: w.lastcols, R: w.lastrows}
123 }
124
125 res := gowid.CalculateRenderSizeFallback(w, size, focus, app)
126 w.lastcols = res.C
127 w.lastrows = res.R
128 return res
129 }
130
131 //======================================================================
132 // Local Variables:
133 // mode: Go
134 // fill-column: 110
135 // End:
0 // Copyright 2021 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
1 // that can be found in the LICENSE file.
2
3 package paragraph
4
5 import (
6 "strings"
7 "testing"
8
9 "github.com/gcla/gowid"
10 "github.com/gcla/gowid/gwtest"
11 "github.com/stretchr/testify/assert"
12 )
13
14 //======================================================================
15
16 func Test1(t *testing.T) {
17 w := New("hello world")
18 c := w.Render(gowid.RenderFlowWith{C: 16}, gowid.NotSelected, gwtest.D)
19 res := strings.Join([]string{
20 "hello world ",
21 }, "\n")
22 assert.Equal(t, res, c.String())
23
24 c = w.Render(gowid.RenderFlowWith{C: 6}, gowid.NotSelected, gwtest.D)
25 res = strings.Join([]string{
26 "hello ",
27 "world ",
28 }, "\n")
29 assert.Equal(t, res, c.String())
30
31 c = w.Render(gowid.RenderFlowWith{C: 9}, gowid.NotSelected, gwtest.D)
32 res = strings.Join([]string{
33 "hello ",
34 "world ",
35 }, "\n")
36 assert.Equal(t, res, c.String())
37
38 c = w.Render(gowid.RenderFlowWith{C: 5}, gowid.NotSelected, gwtest.D)
39 res = strings.Join([]string{
40 "hello",
41 "world",
42 }, "\n")
43 assert.Equal(t, res, c.String())
44
45 c = w.Render(gowid.RenderFlowWith{C: 4}, gowid.NotSelected, gwtest.D)
46 res = strings.Join([]string{
47 "hell",
48 "o wo",
49 "rld ",
50 }, "\n")
51 assert.Equal(t, res, c.String())
52
53 w = New("hello worldatlarge")
54 c = w.Render(gowid.RenderFlowWith{C: 6}, gowid.NotSelected, gwtest.D)
55 res = strings.Join([]string{
56 "hello ",
57 "worlda",
58 "tlarge",
59 }, "\n")
60 assert.Equal(t, res, c.String())
61
62 c = w.Render(gowid.RenderFlowWith{C: 8}, gowid.NotSelected, gwtest.D)
63 res = strings.Join([]string{
64 "hello wo",
65 "rldatlar",
66 "ge ",
67 }, "\n")
68 assert.Equal(t, res, c.String())
69
70 c = w.Render(gowid.RenderFlowWith{C: 12}, gowid.NotSelected, gwtest.D)
71 res = strings.Join([]string{
72 "hello ",
73 "worldatlarge",
74 }, "\n")
75 assert.Equal(t, res, c.String())
76
77 c = w.Render(gowid.RenderFlowWith{C: 13}, gowid.NotSelected, gwtest.D)
78 res = strings.Join([]string{
79 "hello ",
80 "worldatlarge ",
81 }, "\n")
82 assert.Equal(t, res, c.String())
83
84 c = w.Render(gowid.RenderFlowWith{C: 18}, gowid.NotSelected, gwtest.D)
85 res = strings.Join([]string{
86 "hello worldatlarge",
87 }, "\n")
88 assert.Equal(t, res, c.String())
89 }
90
91 //======================================================================
92 // Local Variables:
93 // mode: Go
94 // fill-column: 110
95 // End:
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1111 "github.com/gcla/gowid"
1212 "github.com/gcla/gowid/gwutil"
1313 "github.com/gcla/gowid/vim"
14 "github.com/gdamore/tcell"
14 tcell "github.com/gdamore/tcell/v2"
1515 )
1616
1717 //======================================================================
436436 // What if the Pile is rendered as a RenderFlow? Then you can't specify any weighted widgets
437437
438438 func Render(w IWidget, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) gowid.ICanvas {
439
440439 subfocus := w.Focus()
441440 // if !focus.Focus {
442441 // subfocus = -1
456455 // a pile of width 20, and put it in a column, the next column starts at 21
457456 // TODO - remember which one has focus
458457 res.AppendBelow(canvases[i], i == subfocus, false)
459 if haveMaxRow && res.BoxRows() >= rows.Rows() {
458 if haveMaxRow && res.BoxRows() > rows.Rows() {
460459 trim = true
461460 break
462461 }
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package pile
1515 "github.com/gcla/gowid/widgets/list"
1616 "github.com/gcla/gowid/widgets/selectable"
1717 "github.com/gcla/gowid/widgets/text"
18 "github.com/gdamore/tcell"
18 tcell "github.com/gdamore/tcell/v2"
1919 "github.com/stretchr/testify/assert"
2020 )
2121
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package progress
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package radio
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
88 "fmt"
99
1010 "github.com/gcla/gowid"
11 "github.com/gdamore/tcell"
11 tcell "github.com/gdamore/tcell/v2"
1212 )
1313
1414 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
00 //go:generate statik -src=data
11
2 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
2 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
33 // code is governed by the MIT license that can be found in the LICENSE
44 // file.
55
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
88 "fmt"
99 "strconv"
1010
11 "github.com/araddon/dateparse"
1112 "github.com/gcla/gowid"
1213 "github.com/gcla/gowid/gwutil"
1314 "github.com/gcla/gowid/widgets/columns"
1415 "github.com/gcla/gowid/widgets/isselected"
1516 "github.com/gcla/gowid/widgets/list"
1617 "github.com/gcla/gowid/widgets/pile"
17 "github.com/gdamore/tcell"
18 tcell "github.com/gdamore/tcell/v2"
1819 lru "github.com/hashicorp/golang-lru"
1920 )
2021
108109 }
109110
110111 var _ ICompare = FloatCompare{}
112
113 // DateTimeCompare is a unit type that satisfies ICompare, and can be used
114 // for numerically comparing date/time values.
115 type DateTimeCompare struct{}
116
117 func (s DateTimeCompare) Less(i, j string) bool {
118 x, err1 := dateparse.ParseAny(i)
119 y, err2 := dateparse.ParseAny(j)
120 if err1 == nil && err2 == nil {
121 return x.Before(y)
122 } else {
123 return false
124 }
125 }
126
127 var _ ICompare = DateTimeCompare{}
111128
112129 //======================================================================
113130
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1414 "github.com/gcla/gowid/widgets/fill"
1515 "github.com/gcla/gowid/widgets/selectable"
1616 "github.com/gcla/gowid/widgets/text"
17 "github.com/gdamore/tcell"
17 tcell "github.com/gdamore/tcell/v2"
1818 "github.com/sirupsen/logrus"
1919 "github.com/stretchr/testify/assert"
2020 )
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1616
1717 "github.com/gcla/gowid"
1818 "github.com/gcla/gowid/gwutil"
19 "github.com/gdamore/tcell"
19 tcell "github.com/gdamore/tcell/v2"
2020 "github.com/mattn/go-runewidth"
2121 log "github.com/sirupsen/logrus"
2222 "golang.org/x/text/encoding/charmap"
308308 Height: height,
309309 }
310310 return res
311 }
312
313 func (c *ViewPortCanvas) Duplicate() gowid.ICanvas {
314 res := &ViewPortCanvas{
315 Canvas: c.Canvas.Duplicate().(*gowid.Canvas),
316 Offset: c.Offset,
317 Height: c.Height,
318 }
319 return res
320 }
321
322 func (c *ViewPortCanvas) MergeUnder(c2 gowid.IMergeCanvas, leftOffset, topOffset int, bottomGetsCursor bool) {
323 c.Canvas.MergeUnder(c2, leftOffset, topOffset+c.Offset, bottomGetsCursor)
311324 }
312325
313326 func (v *ViewPortCanvas) BoxRows() int {
414427 }
415428
416429 return len(p), nil
430 }
431
432 func (c *Canvas) Duplicate() gowid.ICanvas {
433 res := &Canvas{}
434 *res = *c
435 res.ViewPortCanvas = c.ViewPortCanvas.Duplicate().(*ViewPortCanvas)
436 res.savedstyles = make(map[string]bool)
437 for k, v := range c.savedstyles {
438 res.savedstyles[k] = v
439 }
440 res.styles = make(map[string]bool)
441 for k, v := range c.styles {
442 res.styles[k] = v
443 }
444 res.tabstops = make([]int, len(c.tabstops))
445 for i, v := range res.tabstops {
446 res.tabstops[i] = v
447 }
448 res.escbuf = make([]byte, len(c.escbuf))
449 for i, v := range res.escbuf {
450 res.escbuf[i] = v
451 }
452 res.utf8Buffer = make([]byte, len(c.utf8Buffer))
453 for i, v := range res.utf8Buffer {
454 res.utf8Buffer[i] = v
455 }
456 return res
417457 }
418458
419459 func (c *Canvas) Reset() {
11861226 c.Offset -= height - c.Height
11871227 c.Height = height
11881228 if c.Height > c.Canvas.BoxRows() {
1189 c.Height = c.Canvas.BoxRows()
1229 c.Canvas.AppendBelow(gowid.NewCanvasOfSize(width, c.Height-c.Canvas.BoxRows()), false, false)
11901230 } else if c.Height < 1 {
11911231 c.Height = 1
11921232 }
12551295 func (c *Canvas) MakeCellFrom(r rune) gowid.Cell {
12561296 var cell gowid.Cell = gowid.MakeCell(r, gowid.MakeTCellColorExt(tcell.ColorDefault), gowid.MakeTCellColorExt(tcell.ColorDefault), gowid.StyleNone)
12571297 if !c.fg.IsNone() {
1258 cell = cell.WithForegroundColor(gowid.MakeTCellColorExt(tcell.Color(c.fg.Val() - 1)))
1298 cell = cell.WithForegroundColor(gowid.MakeTCellColorExt(tcell.Color(c.fg.Val() - 1) + tcell.ColorValid))
12591299 }
12601300 if !c.bg.IsNone() {
1261 cell = cell.WithBackgroundColor(gowid.MakeTCellColorExt(tcell.Color(c.bg.Val() - 1)))
1301 cell = cell.WithBackgroundColor(gowid.MakeTCellColorExt(tcell.Color(c.bg.Val() - 1) + tcell.ColorValid))
12621302 }
12631303 if len(c.styles) > 0 {
12641304 for k, _ := range c.styles {
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1616 "time"
1717 "unsafe"
1818
19 "github.com/creack/pty"
1920 "github.com/gcla/gowid"
2021 "github.com/gcla/gowid/gwutil"
21 "github.com/gdamore/tcell"
22 "github.com/gdamore/tcell/terminfo"
23 "github.com/gdamore/tcell/terminfo/dynamic"
24 "github.com/kr/pty"
22 "github.com/gcla/gowid/widgets/columns"
23 "github.com/gcla/gowid/widgets/holder"
24 "github.com/gcla/gowid/widgets/null"
25 "github.com/gcla/gowid/widgets/vscroll"
26 tcell "github.com/gdamore/tcell/v2"
27 "github.com/gdamore/tcell/v2/terminfo"
28 "github.com/gdamore/tcell/v2/terminfo/dynamic"
2529 log "github.com/sirupsen/logrus"
2630 )
2731
5357 // IHotKeyPersistence determines how long a press of the hotkey will be in effect before
5458 // keyboard user input is sent back to the underlying terminal.
5559 IHotKeyPersistence
60 // IPaste tracks whether the paste start sequence has been seen wthout a matching
61 // paste end sequence
62 IPaste
5663 // HotKeyActive returns true if the hotkey is currently in effect.
5764 HotKeyActive() bool
5865 // SetHotKeyActive sets the state of HotKeyActive.
6774 Scrolling() bool
6875 }
6976
77 type IHotKeyFunctions interface {
78 // Customized handling of hotkey sequences
79 HotKeyFunctions() []HotKeyInputFn
80 }
81
82 type IScrollbar interface {
83 ScrollbarEnabled() bool
84 EnableScrollbar(app gowid.IApp)
85 DisableScrollbar(app gowid.IApp)
86 }
87
7088 type IHotKeyPersistence interface {
7189 HotKeyDuration() time.Duration
7290 }
7492 type IHotKeyProvider interface {
7593 HotKey() tcell.Key
7694 }
95
96 type IPaste interface {
97 PasteState(...bool) bool
98 }
99
100 type HotKeyInputFn func(ev *tcell.EventKey, w IWidget, app gowid.IApp) bool
77101
78102 type HotKeyDuration struct {
79103 D time.Duration
96120 type LEDs struct{}
97121 type Title struct{}
98122 type ProcessExited struct{}
123 type HotKeyCB struct{}
99124
100125 type bell struct{}
101126 type leds struct{}
102127 type title struct{}
128 type hotkey struct{}
103129
104130 type Options struct {
105 Command []string
106 Env []string
107 HotKey IHotKeyProvider
108 HotKeyPersistence IHotKeyPersistence // the period of time a hotKey sticks after the first post-hotKey keypress
109 Scrollback int
131 Command []string
132 Env []string
133 HotKey IHotKeyProvider
134 HotKeyPersistence IHotKeyPersistence // the period of time a hotKey sticks after the first post-hotKey keypress
135 Scrollback int
136 Scrollbar bool // disabled regardless of setting if there is no scrollback
137 HotKeyFns []HotKeyInputFn // allow custom behavior after pressing the hotkey
138 EnableBracketedPaste bool
139 KeyPressToEndScrollMode bool // set to true to enable legacy behavior - when the user has scrolled
140 // back to the prompt, still require a keypress (q or Q) to end scroll-mode.
110141 }
111142
112143 // Widget is a widget that hosts a terminal-based application. The user provides the
130161 hotKeyDownTime time.Time
131162 hotKeyTimer *time.Timer
132163 isScrolling bool
164 paste bool
165 hold *holder.Widget // used if scrollbar is enabled
166 cols *columns.Widget // used if scrollbar is enabled
167 sbar *vscroll.Widget // used if scrollbar is enabled
168 scrollbarTmpOff bool // a simple hack to help with UserInput and Render
133169 Callbacks *gowid.Callbacks
134170 gowid.IsSelectable
135171 }
174210 opts.HotKey = HotKey{tcell.KeyCtrlB}
175211 }
176212
213 if opts.Scrollback <= 0 {
214 opts.Scrollbar = false
215 }
216
217 var persistence IHotKeyPersistence
218 if opts.HotKeyPersistence != nil {
219 persistence = opts.HotKeyPersistence
220 } else {
221 persistence = &HotKeyDuration{
222 D: 2 * time.Second,
223 }
224 }
225
226 // Always allocate so the scrollbar can be turned on later
227 sbar := vscroll.NewExt(vscroll.VerticalScrollbarUnicodeRunes)
228
229 hold := holder.New(null.New())
230
231 cols := columns.New([]gowid.IContainerWidget{
232 &gowid.ContainerWidget{hold, gowid.RenderWithWeight{W: 1}},
233 &gowid.ContainerWidget{sbar, gowid.RenderWithUnits{U: 1}},
234 })
235
177236 res := &Widget{
178237 params: opts,
179238 IHotKeyProvider: opts.HotKey,
180 IHotKeyPersistence: opts.HotKeyPersistence,
239 IHotKeyPersistence: persistence,
181240 terminfo: ti,
241 sbar: sbar,
242 cols: cols,
243 hold: hold,
182244 Callbacks: gowid.NewCallbacks(),
183245 }
246
247 res.hold.SetSubWidget(res, nil)
248 res.cols.SetFocus(nil, 0)
249
250 sbar.OnClickAbove(gowid.WidgetCallback{"cb", res.clickUp})
251 sbar.OnClickBelow(gowid.WidgetCallback{"cb", res.clickDown})
252 sbar.OnClickUpArrow(gowid.WidgetCallback{"cb", res.clickUpArrow})
253 sbar.OnClickDownArrow(gowid.WidgetCallback{"cb", res.clickDownArrow})
184254
185255 var _ gowid.IWidget = res
186256 var _ ITerminal = res
187257 var _ IWidget = res
258 var _ IHotKeyFunctions = res
259 var _ IScrollbar = res
188260 var _ io.Writer = res
189261
190262 return res, nil
204276
205277 func (w *Widget) Terminfo() *terminfo.Terminfo {
206278 return w.terminfo
279 }
280
281 func (w *Widget) ScrollbarEnabled() bool {
282 return w.params.Scrollbar
283 }
284
285 func (w *Widget) EnableScrollbar(app gowid.IApp) {
286 w.params.Scrollbar = true
287 }
288
289 func (w *Widget) DisableScrollbar(app gowid.IApp) {
290 w.params.Scrollbar = false
291 }
292
293 func (w *Widget) HotKeyFunctions() []HotKeyInputFn {
294 return w.params.HotKeyFns
207295 }
208296
209297 func (w *Widget) Bell(app gowid.IApp) {
252340 gowid.RemoveWidgetCallback(w.Callbacks, Bell{}, f)
253341 }
254342
343 func (w *Widget) OnHotKey(f gowid.IWidgetChangedCallback) {
344 gowid.AddWidgetCallback(w.Callbacks, HotKeyCB{}, f)
345 }
346
347 func (w *Widget) RemoveOnHotKey(f gowid.IIdentity) {
348 gowid.RemoveWidgetCallback(w.Callbacks, HotKeyCB{}, f)
349 }
350
351 func (w *Widget) PasteState(b ...bool) bool {
352 if len(b) > 0 {
353 w.paste = b[0]
354 }
355 return w.paste
356 }
357
255358 func (w *Widget) HotKeyActive() bool {
256359 return w.hotKeyDown
257360 }
262365 if w.hotKeyTimer != nil {
263366 w.hotKeyTimer.Stop()
264367 }
368
369 gowid.RunWidgetCallbacks(w.Callbacks, HotKeyCB{}, app, w)
265370
266371 if down {
267372 w.hotKeyDownTime = time.Now()
268373 w.hotKeyTimer = time.AfterFunc(w.HotKeyDuration(), func() {
269374 app.Run(gowid.RunFunction(func(app gowid.IApp) {
270375 w.SetHotKeyActive(app, false)
376 gowid.RunWidgetCallbacks(w.Callbacks, HotKeyCB{}, app, w)
271377 }))
272378 })
273379 }
283389 } else {
284390 lines = w.canvas.ScrollBuffer(dir, false, gwutil.SomeInt(lines))
285391 }
286 // Scrolling is now true if it (a) was previously, or (b) wasn't, but we
287 // scrolled more than one line
288 w.isScrolling = w.isScrolling || lines != 0
392
393 wasScrolling := w.isScrolling
394 if lines != 0 {
395 w.isScrolling = true
396 } else if !w.params.KeyPressToEndScrollMode && dir == ScrollDown {
397 // Disable scroll if we are at the bottom and we tried to scroll down
398 // Thanks @Peter2121 !
399 w.isScrolling = false
400 }
401 if wasScrolling && !w.isScrolling {
402 w.ResetScroll()
403 }
289404 }
290405
291406 func (w *Widget) ResetScroll() {
322437 }
323438
324439 func (w *Widget) UserInput(ev interface{}, size gowid.IRenderSize, focus gowid.Selector, app gowid.IApp) bool {
440 if !w.scrollbarTmpOff && w.params.Scrollbar {
441 w.scrollbarTmpOff = true
442 res := w.cols.UserInput(ev, size, focus, app)
443 w.scrollbarTmpOff = false
444 w.cols.SetFocus(app, 0)
445 return res
446 }
325447 return UserInput(w, ev, size, focus, app)
326448 }
327449
331453 panic(gowid.WidgetSizeError{Widget: w, Size: size, Required: "gowid.IRenderBox"})
332454 }
333455
456 if !w.scrollbarTmpOff && w.params.Scrollbar {
457 w.scrollbarTmpOff = true
458 c := w.cols.Render(size, focus, app)
459 w.scrollbarTmpOff = false
460 return c
461 }
462
334463 w.TouchTerminal(box.BoxColumns(), box.BoxRows(), app)
464
465 w.sbar.Top = w.canvas.Offset
466 w.sbar.Middle = w.canvas.scrollRegionEnd
467 w.sbar.Bottom = gwutil.Max(0, w.canvas.ViewPortCanvas.Canvas.BoxRows()-(box.BoxRows()+w.canvas.Offset))
335468
336469 return w.canvas
337470 }
484617 }))
485618 }})
486619
620 if w.params.EnableBracketedPaste {
621 app.Run(gowid.RunFunction(func(app gowid.IApp) {
622 for _, b := range enablePaste(w.terminfo) {
623 canvas.ProcessByte(b)
624 }
625 }))
626 }
627
487628 go func() {
488629 for {
489630 data := make([]byte, 4096)
518659 if w.master != nil {
519660 w.master.Close()
520661 }
662 }
663
664 func (w *Widget) clickUp(app gowid.IApp, w2 gowid.IWidget) {
665 w.Scroll(ScrollUp, true, 1)
666 }
667
668 func (w *Widget) clickDown(app gowid.IApp, w2 gowid.IWidget) {
669 w.Scroll(ScrollDown, true, 1)
670 }
671
672 func (w *Widget) clickUpArrow(app gowid.IApp, w2 gowid.IWidget) {
673 w.Scroll(ScrollUp, false, 1)
674 }
675
676 func (w *Widget) clickDownArrow(app gowid.IApp, w2 gowid.IWidget) {
677 w.Scroll(ScrollDown, false, 1)
521678 }
522679
523680 //''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
557714 // pressed) then the input will not go to the terminal - it's hotkey
558715 // function processing.
559716 passToTerminal = false
560 res = true
717 res = false
561718 deactivate := false
562 switch evk.Key() {
563 case w.HotKey():
564 deactivate = true
565 case tcell.KeyPgUp:
566 w.Scroll(ScrollUp, true, 0)
567 deactivate = true
568 case tcell.KeyPgDn:
569 w.Scroll(ScrollDown, true, 0)
570 deactivate = true
571 case tcell.KeyUp:
572 w.Scroll(ScrollUp, false, 1)
573 deactivate = true
574 case tcell.KeyDown:
575 w.Scroll(ScrollDown, false, 1)
576 deactivate = true
577 default:
578 res = false
719 if whk, ok := w.(IHotKeyFunctions); ok {
720 for _, fn := range whk.HotKeyFunctions() {
721 res = fn(evk, w, app)
722 if res {
723 deactivate = true
724 break
725 }
726 }
727 }
728 if !res {
729 res = true
730 switch evk.Key() {
731 case w.HotKey():
732 deactivate = true
733 case tcell.KeyPgUp:
734 w.Scroll(ScrollUp, true, 0)
735 deactivate = true
736 case tcell.KeyPgDn:
737 w.Scroll(ScrollDown, true, 0)
738 deactivate = true
739 case tcell.KeyUp:
740 w.Scroll(ScrollUp, false, 1)
741 deactivate = true
742 case tcell.KeyDown:
743 w.Scroll(ScrollDown, false, 1)
744 deactivate = true
745 default:
746 res = false
747 }
579748 }
580749 if deactivate {
581750 w.SetHotKeyActive(app, false)
597766 }
598767 }
599768 if passToTerminal {
600 seq, parsed := TCellEventToBytes(ev, w.Modes(), app.GetLastMouseState(), w.Terminfo())
769 seq, parsed := TCellEventToBytes(ev, w.Modes(), app.GetLastMouseState(), w, w.Terminfo())
601770
602771 if parsed {
603772 _, err := w.Write(seq)
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1111
1212 "github.com/gcla/gowid"
1313 "github.com/gcla/gowid/gwutil"
14 "github.com/gdamore/tcell"
15 "github.com/gdamore/tcell/terminfo"
14 tcell "github.com/gdamore/tcell/v2"
15 "github.com/gdamore/tcell/v2/terminfo"
1616 "github.com/stretchr/testify/assert"
1717 )
1818
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 // Based heavily on vterm.py from urwid
88 "fmt"
99
1010 "github.com/gcla/gowid"
11 "github.com/gdamore/tcell"
12 "github.com/gdamore/tcell/terminfo"
11 tcell "github.com/gdamore/tcell/v2"
12 "github.com/gdamore/tcell/v2/terminfo"
1313 log "github.com/sirupsen/logrus"
1414 )
1515
2323
2424 func (e EventNotSupported) Error() string {
2525 return fmt.Sprintf("Terminal input event %v of type %T not supported yet", e.Event, e.Event)
26 }
27
28 func pasteStart(ti *terminfo.Terminfo) []byte {
29 if ti.PasteStart != "" {
30 return []byte(ti.PasteStart)
31 } else {
32 return []byte("\x1b[200~")
33 }
34 }
35
36 func pasteEnd(ti *terminfo.Terminfo) []byte {
37 if ti.PasteEnd != "" {
38 return []byte(ti.PasteEnd)
39 } else {
40 return []byte("\x1b[201~")
41 }
42 }
43
44 func enablePaste(ti *terminfo.Terminfo) []byte {
45 if ti.EnablePaste != "" {
46 return []byte(ti.EnablePaste)
47 } else {
48 return []byte("\x1b[?2004h")
49 }
50 }
51
52 func disablePaste(ti *terminfo.Terminfo) []byte {
53 if ti.DisablePaste != "" {
54 return []byte(ti.DisablePaste)
55 } else {
56 return []byte("\x1b[?2004l")
57 }
2658 }
2759
2860 // TCellEventToBytes converts TCell's representation of a terminal event to
3365 // subprocess is connected to a tty controlled by gowid. Events from the
3466 // user are parsed by gowid via TCell - they are then translated by this
3567 // function before being written to the TerminalWidget subprocess's tty.
36 func TCellEventToBytes(ev interface{}, mouse IMouseSupport, last gowid.MouseState, ti *terminfo.Terminfo) ([]byte, bool) {
68 func TCellEventToBytes(ev interface{}, mouse IMouseSupport, last gowid.MouseState, paster IPaste, ti *terminfo.Terminfo) ([]byte, bool) {
3769 res := make([]byte, 0)
3870 res2 := false
3971
4072 switch ev := ev.(type) {
73 case *tcell.EventPaste:
74 res2 = true
75 if paster.PasteState() {
76 // Already saw start
77 res = append(res, pasteEnd(ti)...)
78 paster.PasteState(false)
79 } else {
80 res = append(res, pasteStart(ti)...)
81 paster.PasteState(true)
82 }
4183 case *tcell.EventKey:
4284 if ev.Key() < ' ' {
4385 str := []rune{rune(ev.Key())}
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
88 "fmt"
99
1010 "github.com/gcla/gowid"
11 "github.com/gdamore/tcell"
11 tcell "github.com/gdamore/tcell/v2"
1212 )
1313
1414 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package text
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
518518
519519 //======================================================================
520520
521 type ISearchPred interface {
522 CheckNode(IModel, IPos) bool
523 }
524
525 type SearchPred func(IModel, IPos) bool
526
527 func (s SearchPred) CheckNode(tree IModel, pos IPos) bool {
528 return s(tree, pos)
529 }
530
531 func DepthFirstSearch(tree IModel, fn ISearchPred) IPos {
532 pos := NewPos()
533 return depthFirstSearchImpl(tree, pos, fn)
534 }
535
536 func depthFirstSearchImpl(tree IModel, pos *TreePos, fn ISearchPred) IPos {
537 if tree == nil {
538 return nil
539 }
540 if fn.CheckNode(tree, pos) {
541 return pos
542 }
543 cs := tree.Children()
544 tpos := pos.Copy().(*TreePos)
545 tpos.Pos = append(tpos.Pos, 0)
546 i := 0
547 for cs.Next() {
548 tpos.Pos[len(tpos.Pos)-1] = i
549 rpos := depthFirstSearchImpl(cs.Value(), tpos, fn)
550 if rpos != nil {
551 return rpos
552 }
553 i += 1
554 }
555 return nil
556 }
557
558 //======================================================================
559
521560 type IWidgetMaker interface {
522561 MakeWidget(pos IPos, tree IModel) gowid.IWidget
523562 }
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package tree
6868 tt := tp.GetSubStructure(parent1)
6969
7070 assert.Equal(t, leaf1, tt)
71
72 count := 0
73 var pos IPos
74
75 dfs := DepthFirstSearch(parent1, SearchPred(func(t IModel, p IPos) bool {
76 count += 1
77 t2 := t.(*Tree)
78 pos = p
79 return t2.theLeaf == "leaf2"
80 }))
81
82 pos2 := pos.(*TreePos)
83
84 assert.NotNil(t, dfs)
85 assert.Equal(t, 6, count)
86 assert.Equal(t, []int{2}, pos2.Indices())
87
88 st := pos2.GetSubStructure(parent1).(*Tree)
89 assert.Equal(t, "leaf2", st.theLeaf)
90
7191 }
7292
7393 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1010
1111 "github.com/gcla/gowid"
1212 "github.com/gcla/gowid/widgets/fill"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 )
1515
1616 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package vpadding
1010 "github.com/gcla/gowid/gwtest"
1111 "github.com/gcla/gowid/widgets/checkbox"
1212 "github.com/gcla/gowid/widgets/fill"
13 "github.com/gcla/gowid/widgets/pile"
1314 "github.com/gcla/gowid/widgets/text"
14 "github.com/gdamore/tcell"
15 tcell "github.com/gdamore/tcell/v2"
1516 log "github.com/sirupsen/logrus"
1617 "github.com/stretchr/testify/assert"
1718 )
5657 w10 := New(fill.New('x'), gowid.VAlignTop{2}, gowid.RenderWithUnits{U: 4})
5758 c10 := w10.Render(gowid.RenderBox{C: 3, R: 8}, gowid.Focused, gwtest.D)
5859 assert.Equal(t, c10.String(), " \n \nxxx\nxxx\nxxx\nxxx\n \n ")
60
61 w11 := New(fill.New('x'), gowid.VAlignBottom{}, gowid.RenderWithUnits{U: 3})
62 c11 := w11.Render(gowid.RenderBox{C: 3, R: 4}, gowid.Focused, gwtest.D)
63 assert.Equal(t, c11.String(), " \nxxx\nxxx\nxxx")
64
65 p1 := pile.New([]gowid.IContainerWidget{
66 &gowid.ContainerWidget{
67 IWidget: text.New("111"),
68 D: gowid.RenderFixed{},
69 },
70 &gowid.ContainerWidget{
71 IWidget: text.New("222"),
72 D: gowid.RenderFixed{},
73 },
74 &gowid.ContainerWidget{
75 IWidget: text.New("333"),
76 D: gowid.RenderFixed{},
77 },
78 &gowid.ContainerWidget{
79 IWidget: text.New("444"),
80 D: gowid.RenderFixed{},
81 },
82 })
83
84 w12 := New(p1, gowid.VAlignBottom{}, gowid.RenderWithUnits{U: 3})
85 c12 := w12.Render(gowid.RenderBox{C: 3, R: 4}, gowid.Focused, gwtest.D)
86 assert.Equal(t, c12.String(), " \n111\n222\n333")
87
88 w13 := New(p1, gowid.VAlignBottom{}, gowid.RenderWithUnits{U: 3})
89 c13 := w13.Render(gowid.RenderBox{C: 3, R: 2}, gowid.Focused, gwtest.D)
90 assert.Equal(t, c13.String(), "111\n222")
5991
6092 for _, w := range []gowid.IWidget{w1, w2, w3, w4, w5, w6, w7, w8, w9, w10} {
6193 gwtest.RenderBoxManyTimes(t, w, 0, 10, 0, 10)
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source
11 // code is governed by the MIT license that can be found in the LICENSE
22 // file.
33
1010
1111 "github.com/gcla/gowid"
1212 "github.com/gcla/gowid/gwutil"
13 "github.com/gdamore/tcell"
13 tcell "github.com/gdamore/tcell/v2"
1414 )
1515
1616 //======================================================================
0 // Copyright 2019 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
0 // Copyright 2019-2022 Graham Clark. All rights reserved. Use of this source code is governed by the MIT license
11 // that can be found in the LICENSE file.
22
33 package vscroll