MasterWindow.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. package giu
  2. import (
  3. "image"
  4. "image/color"
  5. "runtime"
  6. "time"
  7. "github.com/AllenDang/imgui-go"
  8. "github.com/faiface/mainthread"
  9. "github.com/go-gl/glfw/v3.3/glfw"
  10. "gopkg.in/eapache/queue.v1"
  11. )
  12. // MasterWindowFlags wrapps imgui.GLFWWindowFlags.
  13. type MasterWindowFlags imgui.GLFWWindowFlags
  14. // master window flags.
  15. const (
  16. // Specifies the window will be fixed size.
  17. MasterWindowFlagsNotResizable MasterWindowFlags = MasterWindowFlags(imgui.GLFWWindowFlagsNotResizable)
  18. // Specifies whether the window is maximized.
  19. MasterWindowFlagsMaximized MasterWindowFlags = MasterWindowFlags(imgui.GLFWWindowFlagsMaximized)
  20. // Specifies whether the window will be always-on-top.
  21. MasterWindowFlagsFloating MasterWindowFlags = MasterWindowFlags(imgui.GLFWWindowFlagsFloating)
  22. // Specifies whether the window will be frameless.
  23. MasterWindowFlagsFrameless MasterWindowFlags = MasterWindowFlags(imgui.GLFWWindowFlagsFrameless)
  24. // Specifies whether the window will be transparent.
  25. MasterWindowFlagsTransparent MasterWindowFlags = MasterWindowFlags(imgui.GLFWWindowFlagsTransparent)
  26. )
  27. // DontCare could be used as an argument to (*MasterWindow).SetSizeLimits.
  28. var DontCare int = imgui.GlfwDontCare
  29. // MasterWindow represents a glfw master window
  30. // It is a base for a windows (see Window.go).
  31. type MasterWindow struct {
  32. width int
  33. height int
  34. clearColor [4]float32
  35. title string
  36. platform imgui.Platform
  37. renderer imgui.Renderer
  38. context *imgui.Context
  39. io *imgui.IO
  40. updateFunc func()
  41. }
  42. // NewMasterWindow creates a new master window and initializes GLFW.
  43. // it should be called in main function. For more details and use cases,
  44. // see examples/helloworld/.
  45. func NewMasterWindow(title string, width, height int, flags MasterWindowFlags) *MasterWindow {
  46. initFontAtlasProcessor()
  47. context := imgui.CreateContext(nil)
  48. imgui.ImPlotCreateContext()
  49. imgui.ImNodesCreateContext()
  50. io := imgui.CurrentIO()
  51. io.SetConfigFlags(imgui.ConfigFlagEnablePowerSavingMode | imgui.BackendFlagsRendererHasVtxOffset)
  52. // Disable imgui.ini
  53. io.SetIniFilename("")
  54. p, err := imgui.NewGLFW(io, title, width, height, imgui.GLFWWindowFlags(flags))
  55. if err != nil {
  56. panic(err)
  57. }
  58. // Assign platform to contex
  59. Context.platform = p
  60. r, err := imgui.NewOpenGL3(io, 1.0)
  61. if err != nil {
  62. panic(err)
  63. }
  64. // Create context
  65. Context.renderer = r
  66. // Create font
  67. if len(defaultFonts) == 0 {
  68. io.Fonts().AddFontDefault()
  69. fontAtlas := io.Fonts().TextureDataRGBA32()
  70. r.SetFontTexture(fontAtlas)
  71. } else {
  72. shouldRebuildFontAtlas = true
  73. rebuildFontAtlas()
  74. }
  75. // init texture loading queue
  76. Context.textureLoadingQueue = queue.New()
  77. mw := &MasterWindow{
  78. clearColor: [4]float32{0, 0, 0, 1},
  79. width: width,
  80. height: height,
  81. title: title,
  82. io: &io,
  83. context: context,
  84. platform: p,
  85. renderer: r,
  86. }
  87. mw.SetInputHandler(newInputHandler())
  88. p.SetSizeChangeCallback(mw.sizeChange)
  89. mw.setTheme()
  90. return mw
  91. }
  92. func (w *MasterWindow) setTheme() {
  93. style := imgui.CurrentStyle()
  94. // Scale DPI in windows
  95. if runtime.GOOS == "windows" {
  96. style.ScaleAllSizes(Context.GetPlatform().GetContentScale())
  97. }
  98. imgui.PushStyleVarFloat(imgui.StyleVarWindowRounding, 2)
  99. imgui.PushStyleVarFloat(imgui.StyleVarFrameRounding, 4)
  100. imgui.PushStyleVarFloat(imgui.StyleVarGrabRounding, 4)
  101. imgui.PushStyleVarFloat(imgui.StyleVarFrameBorderSize, 1)
  102. style.SetColor(imgui.StyleColorText, imgui.Vec4{X: 0.95, Y: 0.96, Z: 0.98, W: 1.00})
  103. style.SetColor(imgui.StyleColorTextDisabled, imgui.Vec4{X: 0.36, Y: 0.42, Z: 0.47, W: 1.00})
  104. style.SetColor(imgui.StyleColorWindowBg, imgui.Vec4{X: 0.11, Y: 0.15, Z: 0.17, W: 1.00})
  105. style.SetColor(imgui.StyleColorChildBg, imgui.Vec4{X: 0.15, Y: 0.18, Z: 0.22, W: 1.00})
  106. style.SetColor(imgui.StyleColorPopupBg, imgui.Vec4{X: 0.08, Y: 0.08, Z: 0.08, W: 0.94})
  107. style.SetColor(imgui.StyleColorBorder, imgui.Vec4{X: 0.08, Y: 0.10, Z: 0.12, W: 1.00})
  108. style.SetColor(imgui.StyleColorBorderShadow, imgui.Vec4{X: 0.00, Y: 0.00, Z: 0.00, W: 0.00})
  109. style.SetColor(imgui.StyleColorFrameBg, imgui.Vec4{X: 0.20, Y: 0.25, Z: 0.29, W: 1.00})
  110. style.SetColor(imgui.StyleColorFrameBgHovered, imgui.Vec4{X: 0.12, Y: 0.20, Z: 0.28, W: 1.00})
  111. style.SetColor(imgui.StyleColorFrameBgActive, imgui.Vec4{X: 0.09, Y: 0.12, Z: 0.14, W: 1.00})
  112. style.SetColor(imgui.StyleColorTitleBg, imgui.Vec4{X: 0.09, Y: 0.12, Z: 0.14, W: 0.65})
  113. style.SetColor(imgui.StyleColorTitleBgActive, imgui.Vec4{X: 0.08, Y: 0.10, Z: 0.12, W: 1.00})
  114. style.SetColor(imgui.StyleColorTitleBgCollapsed, imgui.Vec4{X: 0.00, Y: 0.00, Z: 0.00, W: 0.51})
  115. style.SetColor(imgui.StyleColorMenuBarBg, imgui.Vec4{X: 0.15, Y: 0.18, Z: 0.22, W: 1.00})
  116. style.SetColor(imgui.StyleColorScrollbarBg, imgui.Vec4{X: 0.02, Y: 0.02, Z: 0.02, W: 0.39})
  117. style.SetColor(imgui.StyleColorScrollbarGrab, imgui.Vec4{X: 0.20, Y: 0.25, Z: 0.29, W: 1.00})
  118. style.SetColor(imgui.StyleColorScrollbarGrabHovered, imgui.Vec4{X: 0.18, Y: 0.22, Z: 0.25, W: 1.00})
  119. style.SetColor(imgui.StyleColorScrollbarGrabActive, imgui.Vec4{X: 0.09, Y: 0.21, Z: 0.31, W: 1.00})
  120. style.SetColor(imgui.StyleColorCheckMark, imgui.Vec4{X: 0.28, Y: 0.56, Z: 1.00, W: 1.00})
  121. style.SetColor(imgui.StyleColorSliderGrab, imgui.Vec4{X: 0.28, Y: 0.56, Z: 1.00, W: 1.00})
  122. style.SetColor(imgui.StyleColorSliderGrabActive, imgui.Vec4{X: 0.37, Y: 0.61, Z: 1.00, W: 1.00})
  123. style.SetColor(imgui.StyleColorButton, imgui.Vec4{X: 0.20, Y: 0.25, Z: 0.29, W: 1.00})
  124. style.SetColor(imgui.StyleColorButtonHovered, imgui.Vec4{X: 0.28, Y: 0.56, Z: 1.00, W: 1.00})
  125. style.SetColor(imgui.StyleColorButtonActive, imgui.Vec4{X: 0.06, Y: 0.53, Z: 0.98, W: 1.00})
  126. style.SetColor(imgui.StyleColorHeader, imgui.Vec4{X: 0.20, Y: 0.25, Z: 0.29, W: 0.55})
  127. style.SetColor(imgui.StyleColorHeaderHovered, imgui.Vec4{X: 0.26, Y: 0.59, Z: 0.98, W: 0.80})
  128. style.SetColor(imgui.StyleColorHeaderActive, imgui.Vec4{X: 0.26, Y: 0.59, Z: 0.98, W: 1.00})
  129. style.SetColor(imgui.StyleColorSeparator, imgui.Vec4{X: 0.20, Y: 0.25, Z: 0.29, W: 1.00})
  130. style.SetColor(imgui.StyleColorSeparatorHovered, imgui.Vec4{X: 0.10, Y: 0.40, Z: 0.75, W: 0.78})
  131. style.SetColor(imgui.StyleColorSeparatorActive, imgui.Vec4{X: 0.10, Y: 0.40, Z: 0.75, W: 1.00})
  132. style.SetColor(imgui.StyleColorResizeGrip, imgui.Vec4{X: 0.26, Y: 0.59, Z: 0.98, W: 0.25})
  133. style.SetColor(imgui.StyleColorResizeGripHovered, imgui.Vec4{X: 0.26, Y: 0.59, Z: 0.98, W: 0.67})
  134. style.SetColor(imgui.StyleColorResizeGripActive, imgui.Vec4{X: 0.26, Y: 0.59, Z: 0.98, W: 0.95})
  135. style.SetColor(imgui.StyleColorTab, imgui.Vec4{X: 0.11, Y: 0.15, Z: 0.17, W: 1.00})
  136. style.SetColor(imgui.StyleColorTabHovered, imgui.Vec4{X: 0.26, Y: 0.59, Z: 0.98, W: 0.80})
  137. style.SetColor(imgui.StyleColorTabActive, imgui.Vec4{X: 0.20, Y: 0.25, Z: 0.29, W: 1.00})
  138. style.SetColor(imgui.StyleColorTabUnfocused, imgui.Vec4{X: 0.11, Y: 0.15, Z: 0.17, W: 1.00})
  139. style.SetColor(imgui.StyleColorTabUnfocusedActive, imgui.Vec4{X: 0.11, Y: 0.15, Z: 0.17, W: 1.00})
  140. style.SetColor(imgui.StyleColorPlotLines, imgui.Vec4{X: 0.61, Y: 0.61, Z: 0.61, W: 1.00})
  141. style.SetColor(imgui.StyleColorPlotLinesHovered, imgui.Vec4{X: 1.00, Y: 0.43, Z: 0.35, W: 1.00})
  142. style.SetColor(imgui.StyleColorPlotHistogram, imgui.Vec4{X: 0.90, Y: 0.70, Z: 0.00, W: 1.00})
  143. style.SetColor(imgui.StyleColorPlotHistogramHovered, imgui.Vec4{X: 1.00, Y: 0.60, Z: 0.00, W: 1.00})
  144. style.SetColor(imgui.StyleColorTextSelectedBg, imgui.Vec4{X: 0.26, Y: 0.59, Z: 0.98, W: 0.35})
  145. style.SetColor(imgui.StyleColorDragDropTarget, imgui.Vec4{X: 1.00, Y: 1.00, Z: 0.00, W: 0.90})
  146. style.SetColor(imgui.StyleColorNavHighlight, imgui.Vec4{X: 0.26, Y: 0.59, Z: 0.98, W: 1.00})
  147. style.SetColor(imgui.StyleColorNavWindowingHighlight, imgui.Vec4{X: 1.00, Y: 1.00, Z: 1.00, W: 0.70})
  148. style.SetColor(imgui.StyleColorTableHeaderBg, imgui.Vec4{X: 0.12, Y: 0.20, Z: 0.28, W: 1.00})
  149. style.SetColor(imgui.StyleColorTableBorderStrong, imgui.Vec4{X: 0.20, Y: 0.25, Z: 0.29, W: 1.00})
  150. style.SetColor(imgui.StyleColorTableBorderLight, imgui.Vec4{X: 0.20, Y: 0.25, Z: 0.29, W: 0.70})
  151. }
  152. // SetBgColor sets background color of master window.
  153. func (w *MasterWindow) SetBgColor(bgColor color.Color) {
  154. const mask = 0xffff
  155. r, g, b, a := bgColor.RGBA()
  156. w.clearColor = [4]float32{
  157. float32(r) / mask,
  158. float32(g) / mask,
  159. float32(b) / mask,
  160. float32(a) / mask,
  161. }
  162. }
  163. func (w *MasterWindow) sizeChange(width, height int) {
  164. w.render()
  165. }
  166. func (w *MasterWindow) render() {
  167. if !w.platform.IsVisible() || w.platform.IsMinimized() {
  168. return
  169. }
  170. Context.invalidAllState()
  171. defer Context.cleanState()
  172. rebuildFontAtlas()
  173. p := w.platform
  174. r := w.renderer
  175. p.NewFrame()
  176. r.PreRender(w.clearColor)
  177. imgui.NewFrame()
  178. w.updateFunc()
  179. imgui.Render()
  180. r.Render(p.DisplaySize(), p.FramebufferSize(), imgui.RenderedDrawData())
  181. p.PostRender()
  182. }
  183. // Run the main loop to create new frame, process events and call update ui func.
  184. func (w *MasterWindow) run() {
  185. p := w.platform
  186. ticker := time.NewTicker(time.Second / time.Duration(p.GetTPS()))
  187. shouldQuit := false
  188. for !shouldQuit {
  189. mainthread.Call(func() {
  190. // process texture load requests
  191. if Context.textureLoadingQueue != nil && Context.textureLoadingQueue.Length() > 0 {
  192. for Context.textureLoadingQueue.Length() > 0 {
  193. request, ok := Context.textureLoadingQueue.Remove().(textureLoadRequest)
  194. Assert(ok, "MasterWindow", "Run", "processing texture requests: wrong type of texture request")
  195. NewTextureFromRgba(request.img, request.cb)
  196. }
  197. }
  198. p.ProcessEvents()
  199. w.render()
  200. shouldQuit = p.ShouldStop()
  201. })
  202. <-ticker.C
  203. }
  204. }
  205. // GetSize return size of master window.
  206. func (w *MasterWindow) GetSize() (width, height int) {
  207. if w.platform != nil {
  208. if glfwPlatform, ok := w.platform.(*imgui.GLFW); ok {
  209. return glfwPlatform.GetWindow().GetSize()
  210. }
  211. }
  212. return w.width, w.height
  213. }
  214. // GetPos return position of master window.
  215. func (w *MasterWindow) GetPos() (x, y int) {
  216. if w.platform != nil {
  217. if glfwPlatform, ok := w.platform.(*imgui.GLFW); ok {
  218. x, y = glfwPlatform.GetWindow().GetPos()
  219. }
  220. }
  221. return
  222. }
  223. // SetPos sets position of master window.
  224. func (w *MasterWindow) SetPos(x, y int) {
  225. if w.platform != nil {
  226. if glfwPlatform, ok := w.platform.(*imgui.GLFW); ok {
  227. glfwPlatform.GetWindow().SetPos(x, y)
  228. }
  229. }
  230. }
  231. // SetSize sets size of master window.
  232. func (w *MasterWindow) SetSize(x, y int) {
  233. if w.platform != nil {
  234. if glfwPlatform, ok := w.platform.(*imgui.GLFW); ok {
  235. mainthread.CallNonBlock(func() {
  236. glfwPlatform.GetWindow().SetSize(x, y)
  237. })
  238. }
  239. }
  240. }
  241. // SetCloseCallback sets the close callback of the window, which is called when
  242. // the user attempts to close the window, for example by clicking the close
  243. // widget in the title bar.
  244. //
  245. // The close flag is set before this callback is called, but you can modify it at
  246. // any time with returned value of callback function.
  247. //
  248. // Mac OS X: Selecting Quit from the application menu will trigger the close
  249. // callback for all windows.
  250. func (w *MasterWindow) SetCloseCallback(cb func() bool) {
  251. w.platform.SetCloseCallback(cb)
  252. }
  253. // SetDropCallback sets callback when file was droppend into the window.
  254. func (w *MasterWindow) SetDropCallback(cb func([]string)) {
  255. w.platform.SetDropCallback(cb)
  256. }
  257. // Run runs the main loop.
  258. // loopFunc will be used to construct the ui.
  259. // Run should be called at the end of main function, after setting
  260. // up the master window.
  261. func (w *MasterWindow) Run(loopFunc func()) {
  262. mainthread.Run(func() {
  263. Context.isRunning = true
  264. w.updateFunc = loopFunc
  265. Context.isAlive = true
  266. w.run()
  267. Context.isAlive = false
  268. mainthread.Call(func() {
  269. w.renderer.Dispose()
  270. w.platform.Dispose()
  271. imgui.ImNodesDestroyContext()
  272. imgui.ImPlotDestroyContext()
  273. w.context.Destroy()
  274. })
  275. Context.isRunning = false
  276. })
  277. }
  278. // RegisterKeyboardShortcuts registers a global - master window - keyboard shortcuts.
  279. func (w *MasterWindow) RegisterKeyboardShortcuts(s ...WindowShortcut) *MasterWindow {
  280. for _, shortcut := range s {
  281. Context.InputHandler.RegisterKeyboardShortcuts(Shortcut{
  282. Key: shortcut.Key,
  283. Modifier: shortcut.Modifier,
  284. Callback: shortcut.Callback,
  285. IsGlobal: GlobalShortcut,
  286. })
  287. }
  288. return w
  289. }
  290. // SetIcon sets the icon of the specified window. If passed an array of candidate images,
  291. // those of or closest to the sizes desired by the system are selected. If no images are
  292. // specified, the window reverts to its default icon.
  293. //
  294. // The image is ideally provided in the form of *image.NRGBA.
  295. // The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight
  296. // bits per channel with the red channel first. They are arranged canonically
  297. // as packed sequential rows, starting from the top-left corner. If the image
  298. // type is not *image.NRGBA, it will be converted to it.
  299. //
  300. // The desired image sizes varies depending on platform and system settings. The selected
  301. // images will be rescaled as needed. Good sizes include 16x16, 32x32 and 48x48.
  302. func (w *MasterWindow) SetIcon(icons []image.Image) {
  303. w.platform.SetIcon(icons)
  304. }
  305. // SetSizeLimits sets the size limits of the client area of the specified window.
  306. // If the window is full screen or not resizable, this function does nothing.
  307. //
  308. // The size limits are applied immediately and may cause the window to be resized.
  309. // To specify only a minimum size or only a maximum one, set the other pair to giu.DontCare.
  310. // To disable size limits for a window, set them all to giu.DontCare.
  311. func (w *MasterWindow) SetSizeLimits(minw, minh, maxw, maxh int) {
  312. w.platform.SetSizeLimits(minw, minh, maxw, maxh)
  313. }
  314. // SetTitle updates master window's title.
  315. func (w *MasterWindow) SetTitle(title string) {
  316. w.platform.SetTitle(title)
  317. }
  318. // Close will savely close the master window.
  319. func (w *MasterWindow) Close() {
  320. w.SetShouldClose(true)
  321. }
  322. // SetShouldClose sets whether master window should be closed.
  323. func (w *MasterWindow) SetShouldClose(v bool) {
  324. w.platform.SetShouldStop(v)
  325. }
  326. // SetInputHandler allows to change default input handler.
  327. // see InputHandler.go.
  328. func (w *MasterWindow) SetInputHandler(handler InputHandler) {
  329. Context.InputHandler = handler
  330. w.platform.SetInputCallback(func(key glfw.Key, modifier glfw.ModifierKey, action glfw.Action) {
  331. if action == glfw.Press {
  332. handler.Handle(Key(key), Modifier(modifier))
  333. }
  334. })
  335. }