func (a *App) Quit() {
	a.closingMtx.Lock()
	defer a.closingMtx.Unlock()
	a.closing = true
	close(a.AfterRenderEvents)
}
Quit will terminate the gowid main loop.
package gowid
import (
	"fmt"
	"os"
	"path/filepath"
	"runtime/debug"
	"strings"
	"sync"
	"time"
	"github.com/gdamore/tcell"
	log "github.com/sirupsen/logrus"
)
type IGetScreen interface {
	GetScreen() tcell.Screen
}
type IColorMode interface {
	GetColorMode() ColorMode
}
type IPalette interface {
	CellStyler(name string) (ICellStyler, bool)
	RangeOverPalette(f func(key string, value ICellStyler) bool)
}
type IRenderContext interface {
	IPalette
	IColorMode
}
type IApp interface {
	IRenderContext
	IGetScreen
	ISettableComposite
	Quit()                                                      
	Redraw()                                                    
	Sync()                                                      
	SetColorMode(mode ColorMode)                                
	Run(f IAfterRenderEvent) error                              
	SetClickTarget(k tcell.ButtonMask, w IIdentityWidget) bool  
	ClickTarget(func(tcell.ButtonMask, IIdentityWidget))        
	GetMouseState() MouseState                                  
	GetLastMouseState() MouseState                              
	RegisterMenu(menu IMenuCompatible)                          
	UnregisterMenu(menu IMenuCompatible) bool                   
	InCopyMode(...bool) bool                                    
	CopyModeClaimedAt(...int) int                               
	CopyModeClaimedBy(...IIdentity) IIdentity                   
	RefreshCopyMode()                                           
	Clips() []ICopyResult                                       
	CopyLevel(...int) int                                       
}
type App struct {
	IPalette                                  
	screen            tcell.Screen            
	TCellEvents       chan tcell.Event        
	AfterRenderEvents chan IAfterRenderEvent  
	closing           bool                    
	closingMtx        sync.Mutex              
	viewPlusMenus     IWidget                 
	view              IWidget                 
	colorMode         ColorMode               
	inCopyMode        bool                    
	copyClaimed       int                     
	copyClaimedBy     IIdentity
	copyLevel         int
	refreshCopy       bool
	prevWasMouseMove  bool  
	lastMouse    MouseState     
	MouseState                  
	ClickTargets                
	log          log.StdLogger  
}
var _ IApp = (*App)(nil)
type AppArgs struct {
	View         IWidget
	Palette      IPalette
	Log          log.StdLogger
	DontActivate bool
}
type IUnhandledInput interface {
	UnhandledInput(app IApp, ev interface{}) bool
}
type UnhandledInputFunc func(app IApp, ev interface{}) bool
func (f UnhandledInputFunc) UnhandledInput(app IApp, ev interface{}) bool {
	return f(app, ev)
}
var IgnoreUnhandledInput UnhandledInputFunc = func(app IApp, ev interface{}) bool {
	return false
}
type ClickTargets struct {
	click map[tcell.ButtonMask][]IIdentityWidget  
}
func MakeClickTargets() ClickTargets {
	return ClickTargets{
		click: make(map[tcell.ButtonMask][]IIdentityWidget),
	}
}
func (t ClickTargets) SetClickTarget(k tcell.ButtonMask, w IIdentityWidget) bool {
	targets, ok := t.click[k]
	if !ok {
		targets = make([]IIdentityWidget, 1)
		targets[0] = w
	} else {
		targets = append(targets, w)
	}
	t.click[k] = targets
	return !ok
}
func (t ClickTargets) ClickTarget(f func(tcell.ButtonMask, IIdentityWidget)) {
	for k, v := range t.click {
		for _, t := range v {
			f(k, t)
		}
	}
}
func (t ClickTargets) DeleteClickTargets(k tcell.ButtonMask) {
	if ws, ok := t.click[k]; ok {
		for _, w := range ws {
			if w2, ok := w.(IClickTracker); ok {
				w2.SetClickPending(false)
			}
		}
		delete(t.click, k)
	}
}
type MouseState struct {
	MouseLeftClicked   bool
	MouseMiddleClicked bool
	MouseRightClicked  bool
}
func (m MouseState) String() string {
	return fmt.Sprintf("LeftClicked: %v, MiddleClicked: %v, RightClicked: %v",
		m.MouseLeftClicked,
		m.MouseMiddleClicked,
		m.MouseRightClicked,
	)
}
func (m MouseState) NoButtonClicked() bool {
	return !m.LeftIsClicked() && !m.MiddleIsClicked() && !m.RightIsClicked()
}
func (m MouseState) LeftIsClicked() bool {
	return m.MouseLeftClicked
}
func (m MouseState) MiddleIsClicked() bool {
	return m.MouseMiddleClicked
}
func (m MouseState) RightIsClicked() bool {
	return m.MouseRightClicked
}
func NewApp(args AppArgs) (rapp *App, rerr error) {
	app, err := newApp(args)
	if err != nil {
		return nil, err
	}
	return app, nil
}
func newApp(args AppArgs) (rapp *App, rerr error) {
	screen, err := tcell.NewScreen()
	if err != nil {
		rerr = WithKVs(err, map[string]interface{}{"TERM": os.Getenv("TERM")})
		return
	}
	var palette IPalette = args.Palette
	if palette == nil {
		palette = make(Palette)
	}
	tch := make(chan tcell.Event, 1000)
	wch := make(chan IAfterRenderEvent, 1000)
	clicks := MakeClickTargets()
	if args.Log == nil {
		logname := filepath.Base(os.Args[0])
		logname = fmt.Sprintf("%s.log", strings.TrimSuffix(logname, filepath.Ext(logname)))
		logfile, err := os.Create(logname)
		if err != nil {
			return nil, err
		}
		logger := log.New()
		logger.Out = logfile
		args.Log = logger
	}
	res := &App{
		IPalette:          palette,
		screen:            screen,
		TCellEvents:       tch,
		AfterRenderEvents: wch,
		closing:           false,
		view:              args.View,
		viewPlusMenus:     args.View,
		colorMode:         Mode256Colors,
		ClickTargets:      clicks,
		log:               args.Log,
	}
	if !args.DontActivate {
		if err = res.initScreen(); err != nil {
			return nil, err
		}
		res.initColorMode()
	}
	screen.Clear()
	rapp = res
	return
}
func (a *App) initColorMode() {
	cols := a.screen.Colors()
	switch {
	case cols > 256:
		a.SetColorMode(Mode24BitColors)
	case cols == 256:
		a.SetColorMode(Mode256Colors)
	case cols == 88:
		a.SetColorMode(Mode88Colors)
	case cols == 16:
		a.SetColorMode(Mode16Colors)
	case cols < 0:
		a.SetColorMode(ModeMonochrome)
	default:
		a.SetColorMode(Mode8Colors)
	}
}
func (a *App) GetScreen() tcell.Screen {
	return a.screen
}
func (a *App) RefreshCopyMode() {
	a.refreshCopy = true
}
func (a *App) CopyLevel(lvl ...int) int {
	if len(lvl) > 0 {
		a.copyLevel = lvl[0]
	}
	return a.copyLevel
}
func (a *App) InCopyMode(on ...bool) bool {
	if len(on) > 0 {
		a.inCopyMode = on[0]
	}
	return a.inCopyMode
}
func (a *App) CopyModeClaimedAt(lvl ...int) int {
	if len(lvl) > 0 {
		a.copyClaimed = lvl[0]
	}
	return a.copyClaimed
}
func (a *App) CopyModeClaimedBy(id ...IIdentity) IIdentity {
	if len(id) > 0 {
		a.copyClaimedBy = id[0]
	}
	return a.copyClaimedBy
}
func (a *App) SetSubWidget(widget IWidget, app IApp) {
	a.view = widget
	if a.viewPlusMenus == nil {
		a.viewPlusMenus = widget
	}
}
func (a *App) SubWidget() IWidget {
	return a.view
}
func (a *App) SetPalette(palette IPalette) {
	a.IPalette = palette
}
func (a *App) GetPalette() IPalette {
	return a.IPalette
}
func (a *App) GetMouseState() MouseState {
	return a.MouseState
}
func (a *App) GetLastMouseState() MouseState {
	return a.lastMouse
}
func (a *App) SetColorMode(mode ColorMode) {
	a.colorMode = mode
}
func (a *App) GetColorMode() ColorMode {
	return a.colorMode
}
func (a *App) TerminalSize() (x, y int) {
	x, y = a.screen.Size()
	if x == 0 && y == 0 {
		x = 80
		y = 24
	}
	return
}
type LogField struct {
	Name string
	Val  interface{}
}
type CopyModeEvent struct{}
func (c CopyModeEvent) When() time.Time {
	return time.Time{}
}
type ICopyModeClips interface {
	Collect([]ICopyResult)
}
type CopyModeClipsFn func([]ICopyResult)
func (f CopyModeClipsFn) Collect(clips []ICopyResult) {
	f(clips)
}
type CopyModeClipsEvent struct {
	Action ICopyModeClips
}
func (c CopyModeClipsEvent) When() time.Time {
	return time.Time{}
}
type privateId struct{}
func (n privateId) ID() interface{} {
	return n
}
func (a *App) Clips() []ICopyResult {
	res := make([]ICopyResult, 0)
	cb := CopyModeClipsFn(func(clips []ICopyResult) {
		res = append(res, clips...)
	})
	unh := UnhandledInputFunc(func(app IApp, ev interface{}) bool {
		return true
	})
	a.handleInputEvent(
		CopyModeClipsEvent{
			Action: cb,
		},
		unh,
	)
	return res
}
func (a *App) HandleTCellEvent(ev interface{}, unhandled IUnhandledInput) {
	switch ev := ev.(type) {
	case *tcell.EventKey:
		debug.SetGCPercent(-1)
		defer debug.SetGCPercent(100)
		cm := a.InCopyMode()
		a.handleInputEvent(ev, unhandled)
		newCopyMode := (!cm && a.InCopyMode())
		if newCopyMode || a.refreshCopy {
			a.copyLevel = 0   
			if newCopyMode {  
				a.copyClaimed = 100000  
				a.copyClaimedBy = privateId{}
			}
			a.handleInputEvent(CopyModeEvent{}, unhandled)
			a.refreshCopy = false
		}
		a.RedrawTerminal()
	case *tcell.EventMouse:
		if !a.prevWasMouseMove || ev.Modifiers() != 0 || ev.Buttons() != 0 {
			switch ev.Buttons() {
			case tcell.Button1:
				a.MouseLeftClicked = true
			case tcell.Button2:
				a.MouseMiddleClicked = true
			case tcell.Button3:
				a.MouseRightClicked = true
			default:
			}
			debug.SetGCPercent(-1)
			defer debug.SetGCPercent(100)
			a.handleInputEvent(ev, unhandled)
			if ev.Buttons() == tcell.ButtonNone {
				a.ClickTargets.DeleteClickTargets(tcell.Button1)
				a.ClickTargets.DeleteClickTargets(tcell.Button2)
				a.ClickTargets.DeleteClickTargets(tcell.Button3)
			}
			a.lastMouse = a.MouseState
			a.MouseState = MouseState{}
			a.RedrawTerminal()
		}
	case *tcell.EventResize:
		if flog, ok := a.log.(log.FieldLogger); ok {
			flog.WithField("event", ev).Infof("Terminal was resized")
		} else {
			a.log.Printf("Terminal was resized\n")
		}
		a.RedrawTerminal()
	case *tcell.EventInterrupt:
		if flog, ok := a.log.(log.FieldLogger); ok {
			flog.WithField("event", ev).Infof("Interrupt event from tcell")
		} else {
			a.log.Printf("Interrupt event from tcell: %v\n", ev)
		}
	case *tcell.EventError:
		if flog, ok := a.log.(log.FieldLogger); ok {
			flog.WithField("event", ev).WithField("error", ev.Error()).Errorf("Error event from tcell")
		} else {
			a.log.Printf("Error event from tcell: %v, %v\n", ev, ev.Error())
		}
	default:
		if flog, ok := a.log.(log.FieldLogger); ok {
			flog.WithField("event", ev).Infof("Unanticipated event from tcell")
		} else {
			a.log.Printf("Unanticipated event from tcell: %v\n", ev)
		}
	}
	if ev, ok := ev.(*tcell.EventMouse); ok && ev.Buttons() == 0 && ev.Modifiers() == 0 {
		a.prevWasMouseMove = true
	} else {
		a.prevWasMouseMove = false
	}
}
func (a *App) Close() {
	a.screen.Fini()
}
func (a *App) StartTCellEvents(quit <-chan Unit, wg *sync.WaitGroup) {
	wg.Add(1)
	go func(quit <-chan Unit) {
		defer wg.Done()
	Loop:
		for {
			a.TCellEvents <- a.screen.PollEvent()
			select {
			case <-quit:
				break Loop
			default:
			}
		}
	}(quit)
}
func (a *App) StopTCellEvents(quit chan<- Unit, wg *sync.WaitGroup) {
	quit <- Unit{}
	a.screen.PostEventWait(tcell.NewEventInterrupt(nil))
	wg.Wait()
}
func (a *App) SimpleMainLoop() {
	a.MainLoop(UnhandledInputFunc(HandleQuitKeys))
}
func HandleQuitKeys(app IApp, event interface{}) bool {
	handled := false
	if ev, ok := event.(*tcell.EventKey); ok {
		if ev.Key() == tcell.KeyCtrlC || ev.Key() == tcell.KeyEsc || ev.Rune() == 'q' || ev.Rune() == 'Q' {
			app.Quit()
			handled = true
		}
	}
	return handled
}
type AppRunner struct {
	app     *App
	wg      sync.WaitGroup
	started bool
	quitCh  chan Unit
}
func (a *App) Runner() *AppRunner {
	res := &AppRunner{
		app:    a,
		quitCh: make(chan Unit, 100),
	}
	return res
}
func (st *AppRunner) Start() {
	st.app.StartTCellEvents(st.quitCh, &st.wg)
	st.started = true
}
func (st *AppRunner) Stop() {
	if st.started {
		st.app.StopTCellEvents(st.quitCh, &st.wg)
		st.started = false
	}
}
func (a *App) MainLoop(unhandled IUnhandledInput) {
	defer a.Close()
	st := a.Runner()
	st.Start()
	defer st.Stop()
	a.handleEvents(unhandled)
}
func (a *App) RunThenRenderEvent(ev IAfterRenderEvent) {
	ev.RunThenRenderEvent(a)
	a.RedrawTerminal()
}
func (a *App) handleEvents(unhandled IUnhandledInput) {
Loop:
	for {
		select {
		case ev := <-a.TCellEvents:
			a.HandleTCellEvent(ev, unhandled)
		case ev := <-a.AfterRenderEvents:
			if ev == nil {
				break Loop
			}
			a.RunThenRenderEvent(ev)
		}
	}
}
func (a *App) handleInputEvent(ev interface{}, unhandled IUnhandledInput) {
	switch ev.(type) {
	case *tcell.EventKey, *tcell.EventMouse:
		x, y := a.TerminalSize()
		handled := UserInputIfSelectable(a.viewPlusMenus, ev, RenderBox{C: x, R: y}, Focused, a)
		if !handled {
			handled = unhandled.UnhandledInput(a, ev)
			if !handled {
				if flog, ok := a.log.(log.FieldLogger); ok {
					flog.WithField("event", ev).Debugf("Input was not handled")
				} else {
					a.log.Printf("Input was not handled: %v\n", ev)
				}
			}
		}
	default:
		x, y := a.TerminalSize()
		UserInputIfSelectable(a.viewPlusMenus, ev, RenderBox{C: x, R: y}, Focused, a)
	}
}
func (a *App) Sync() {
	a.screen.Sync()
}
func (a *App) RedrawTerminal() {
	RenderRoot(a.viewPlusMenus, a)
	a.screen.Show()
}
func (a *App) RegisterMenu(menu IMenuCompatible) {
	menu.SetSubWidget(a.viewPlusMenus, a)
	a.viewPlusMenus = menu
}
type menuView struct {
	*App
}
func (a *menuView) SetSubWidget(widget IWidget, app IApp) {
	a.viewPlusMenus = widget
}
func (a *App) unregisterMenu(cur ISettableComposite, removeMe IMenuCompatible) bool {
	res := true
	for {
		if sm, ok := cur.SubWidget().(IMenuCompatible); ok {
			if sm == removeMe {
				cur.SetSubWidget(sm.SubWidget(), a)
				break
			} else {
				cur = sm
			}
		} else {
			res = false
			break
		}
	}
	return res
}
func (a *App) UnregisterMenu(menu IMenuCompatible) bool {
	return a.unregisterMenu(&menuView{a}, menu)
}
type RunFunction func(IApp)
type IAfterRenderEvent interface {
	RunThenRenderEvent(IApp)
}
func (f RunFunction) RunThenRenderEvent(app IApp) {
	f(app)
}
var AppClosingErr = fmt.Errorf("App is closing - no more events accepted.")
func (a *App) Run(f IAfterRenderEvent) error {
	a.closingMtx.Lock()
	defer a.closingMtx.Unlock()
	if !a.closing {
		a.AfterRenderEvents <- f
		return nil
	}
	return AppClosingErr
}
func (a *App) Redraw() {
	a.Run(RunFunction(func(IApp) {}))
}