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 }