github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/framework/masterwindow.go (about)

     1  package framework
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"image"
     7  	"image/draw"
     8  	"image/png"
     9  	"os"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/gop9/olt/framework/command"
    15  	"github.com/gop9/olt/framework/rect"
    16  	nstyle "github.com/gop9/olt/framework/style"
    17  )
    18  
    19  type MasterWindow interface {
    20  	context() *context
    21  
    22  	Main()
    23  	Changed()
    24  	Close()
    25  	Closed() bool
    26  	ActivateEditor(ed *TextEditor)
    27  
    28  	Style() *nstyle.Style
    29  	SetStyle(*nstyle.Style)
    30  
    31  	GetPerf() bool
    32  	SetPerf(bool)
    33  
    34  	Input() *Input
    35  
    36  	PopupOpen(title string, flags WindowFlags, rect rect.Rect, scale bool, updateFn UpdateFn)
    37  
    38  	Walk(WindowWalkFn)
    39  	ResetWindows() *DockSplit
    40  
    41  	Lock()
    42  	Unlock()
    43  }
    44  
    45  func NewMasterWindow(flags WindowFlags, title string, updatefn UpdateFn) MasterWindow {
    46  	return NewMasterWindowSize(flags, title, image.Point{640, 480}, updatefn)
    47  }
    48  
    49  type WindowWalkFn func(title string, data interface{}, docked bool, splitSize int, rect rect.Rect)
    50  
    51  type masterWindowCommon struct {
    52  	ctx *context
    53  
    54  	layout panel
    55  
    56  	// show performance counters
    57  	Perf bool
    58  
    59  	uilock sync.Mutex
    60  
    61  	prevCmds []command.Command
    62  }
    63  
    64  func (mw *masterWindowCommon) masterWindowCommonInit(ctx *context, flags WindowFlags, updatefn UpdateFn, wnd MasterWindow) {
    65  	ctx.Input.Mouse.valid = true
    66  	ctx.DockedWindows.Split.MinSize = 40
    67  
    68  	mw.layout.Flags = flags
    69  
    70  	ctx.setupMasterWindow(&mw.layout, updatefn)
    71  
    72  	mw.ctx = ctx
    73  	mw.ctx.mw = wnd
    74  
    75  	mw.SetStyle(nstyle.FromTheme(nstyle.DefaultTheme, 1.0))
    76  }
    77  
    78  func (mw *masterWindowCommon) context() *context {
    79  	return mw.ctx
    80  }
    81  
    82  func (mw *masterWindowCommon) Walk(fn WindowWalkFn) {
    83  	mw.ctx.Walk(fn)
    84  }
    85  
    86  func (mw *masterWindowCommon) ResetWindows() *DockSplit {
    87  	return mw.ctx.ResetWindows()
    88  }
    89  
    90  func (mw *masterWindowCommon) Input() *Input {
    91  	return &mw.ctx.Input
    92  }
    93  
    94  func (mw *masterWindowCommon) ActivateEditor(ed *TextEditor) {
    95  	mw.ctx.activateEditor = ed
    96  }
    97  
    98  func (mw *masterWindowCommon) Style() *nstyle.Style {
    99  	return &mw.ctx.Style
   100  }
   101  
   102  func (mw *masterWindowCommon) SetStyle(style *nstyle.Style) {
   103  	mw.ctx.Style = *style
   104  	mw.ctx.Style.Defaults()
   105  }
   106  
   107  func (mw *masterWindowCommon) GetPerf() bool {
   108  	return mw.Perf
   109  }
   110  
   111  func (mw *masterWindowCommon) SetPerf(perf bool) {
   112  	mw.Perf = perf
   113  }
   114  
   115  // Forces an update of the window.
   116  func (mw *masterWindowCommon) Changed() {
   117  	atomic.AddInt32(&mw.ctx.changed, 1)
   118  }
   119  
   120  func (mw *masterWindowCommon) Lock() {
   121  	mw.uilock.Lock()
   122  }
   123  
   124  func (mw *masterWindowCommon) Unlock() {
   125  	mw.uilock.Unlock()
   126  }
   127  
   128  // Opens a popup window inside win. Will return true until the
   129  // popup window is closed.
   130  // The contents of the popup window will be updated by updateFn
   131  func (mw *masterWindowCommon) PopupOpen(title string, flags WindowFlags, rect rect.Rect, scale bool, updateFn UpdateFn) {
   132  	go func() {
   133  		mw.ctx.mw.Lock()
   134  		defer mw.ctx.mw.Unlock()
   135  		mw.ctx.popupOpen(title, flags, rect, scale, updateFn)
   136  		mw.ctx.mw.Changed()
   137  	}()
   138  }
   139  
   140  var frameCnt int
   141  
   142  func (w *masterWindowCommon) dumpFrame(wimg *image.RGBA, t0, t1, te time.Time, nprimitives int) {
   143  	bounds := image.Rect(w.ctx.Input.Mouse.Pos.X, w.ctx.Input.Mouse.Pos.Y, w.ctx.Input.Mouse.Pos.X+10, w.ctx.Input.Mouse.Pos.Y+10)
   144  
   145  	draw.Draw(wimg, bounds, image.White, bounds.Min, draw.Src)
   146  
   147  	if fh, err := os.Create(fmt.Sprintf("framedump/frame%03d.png", frameCnt)); err == nil {
   148  		png.Encode(fh, wimg)
   149  		fh.Close()
   150  	}
   151  
   152  	if fh, err := os.Create(fmt.Sprintf("framedump/frame%03d.txt", frameCnt)); err == nil {
   153  		wr := bufio.NewWriter(fh)
   154  		fps := 1.0 / te.Sub(t0).Seconds()
   155  		tot := time.Duration(0)
   156  		fmt.Fprintf(wr, "# Update %0.4fms = %0.4f updatefn + %0.4f draw (%d primitives) [max fps %0.2f]\n", te.Sub(t0).Seconds()*1000, t1.Sub(t0).Seconds()*1000, te.Sub(t1).Seconds()*1000, nprimitives, fps)
   157  		for i := range w.prevCmds {
   158  			fmt.Fprintf(wr, "%0.2fms %#v\n", w.ctx.cmdstim[i].Seconds()*1000, w.prevCmds[i])
   159  			tot += w.ctx.cmdstim[i]
   160  		}
   161  		fmt.Fprintf(wr, "sanity check %0.2fms\n", tot.Seconds()*1000)
   162  		wr.Flush()
   163  		fh.Close()
   164  	}
   165  
   166  	frameCnt++
   167  }
   168  
   169  // compares cmds to the last draw frame, returns true if there is a change
   170  func (w *masterWindowCommon) drawChanged() bool {
   171  
   172  	contextAllCommands(w.ctx)
   173  	w.ctx.Reset()
   174  
   175  	cmds := w.ctx.cmds
   176  
   177  	if len(cmds) != len(w.prevCmds) {
   178  		return true
   179  	}
   180  
   181  	for i := range cmds {
   182  		if cmds[i].Kind != w.prevCmds[i].Kind {
   183  			return true
   184  		}
   185  
   186  		cmd := &cmds[i]
   187  		pcmd := &w.prevCmds[i]
   188  
   189  		switch cmds[i].Kind {
   190  		case command.ScissorCmd:
   191  			if *pcmd != *cmd {
   192  				return true
   193  			}
   194  
   195  		case command.LineCmd:
   196  			if *pcmd != *cmd {
   197  				return true
   198  			}
   199  
   200  		case command.RectFilledCmd:
   201  			if i == 0 {
   202  				cmd.RectFilled.Color.A = 0xff
   203  			}
   204  			if *pcmd != *cmd {
   205  				return true
   206  			}
   207  
   208  		case command.TriangleFilledCmd:
   209  			if *pcmd != *cmd {
   210  				return true
   211  			}
   212  
   213  		case command.CircleFilledCmd:
   214  			if *pcmd != *cmd {
   215  				return true
   216  			}
   217  
   218  		case command.ImageCmd:
   219  			if *pcmd != *cmd {
   220  				return true
   221  			}
   222  
   223  		case command.TextCmd:
   224  			if *pcmd != *cmd {
   225  				return true
   226  			}
   227  
   228  		default:
   229  			panic(UnknownCommandErr)
   230  		}
   231  	}
   232  
   233  	return false
   234  }