view: render map entities
[d2df-maped.git] / src / gui.nim
blobe68e8b823a9c457a3727590d0b27d3274698e1ba
1 import
2   std/math,
3   thirdparty/imgui,
4   utils, gfx, map, vfs, config,
5   gui/[common, controls, file_dialog, wad_dialog, map_dialog, message_box]
7 export
8   common, controls, file_dialog, wad_dialog, map_dialog, message_box, imgui
10 const
11   customBarWidth* = 8
12   menuBarHeight* = 20
14 var
15   customBarColBg*: DFColor = colDkGrey
16   customBarColFg*: DFColor = colGrey
17   customBarColMove*: DFColor = colWhite
18   dockSizeLeft* = 0
19   dockSizeRight* = 150
20   dockSizeBottom* = 150
21   selection*: seq[MapObject] = @[]
23 type
24   CustomScrollBar* = object
25     axis: int # 0=x, 1=y
26     start: DFPoint
27     size: DFSize
28     barSize: DFSize
29     barStart: DFPoint
30     maxSize: int
31     active: bool
33 type
34   ActionProc = proc (): void
36 var
37   confirmBox: MessageBox
38   confirmAction: ActionProc = nil
40 proc toDFColor*(c: ImVec4): DFColor =
41   result.r = uint8(c.x * 255f)
42   result.g = uint8(c.y * 255f)
43   result.b = uint8(c.z * 255f)
44   result.a = uint8(c.w * 255f)
46 proc shutdown*() =
47   discard
49 proc init*() =
50   addExitProc(gui.shutdown)
51   let style = imgui.getStyle()
52   customBarColBg = style.colors[ImGuiCol.ScrollbarBg.int32].toDFColor()
53   customBarColBg.a = 0xFF'u8
54   customBarColFg = style.colors[ImGuiCol.ScrollbarGrab.int32].toDFColor()
55   customBarColMove = style.colors[ImGuiCol.ScrollbarGrabActive.int32].toDFColor()
57 proc openDiscardWarning() =
58   confirmBox.open(mbQuestion, "Are you sure you want to discard the current map?")
60 proc emitMapOpenDialog() =
61   defaultFileDialog.open("Select WAD", "maps", archiveExts, false, "")
63 proc emitMapSelectDialog() =
64   discard
66 proc emitMainFileMenu() =
67   if imgui.menuItem("New Map", "Ctrl+N"):
68     openDiscardWarning()
69     confirmAction = map.create
70   if imgui.menuItem("Open Map", "Ctrl+O"):
71     openDiscardWarning()
72     confirmAction = emitMapOpenDialog
73   if imgui.menuItem("Select Map"):
74     openDiscardWarning()
75     confirmAction = emitMapSelectDialog
76   if imgui.menuItem("Reopen Map", "F5"):
77     openDiscardWarning()
78     confirmAction = map.reopen
79   imgui.separator()
80   if imgui.menuItem("Save Map", "Ctrl+S"):
81     discard
82   if imgui.menuItem("Save Map As..."):
83     discard
84   if imgui.menuItem("Pack Map"):
85     discard
86   if imgui.menuItem("Delete Map from WAD..."):
87     discard
88   imgui.separator()
89   if imgui.menuItem("Exit", "F10"):
90     confirmBox.open(mbQuestion, "Are you sure you want to quit?")
91     confirmAction = nil
93 proc emitMainEditMenu() =
94   if imgui.menuItem("Undo", "Ctrl+Z"):
95     discard
96   if imgui.menuItem("Redo", "Ctrl+Y"):
97     discard
98   imgui.separator()
99   if imgui.menuItem("Copy", "Ctrl+C"):
100     discard
101   if imgui.menuItem("Cut", "Ctrl+X"):
102     discard
103   if imgui.menuItem("Paste", "Ctrl+V"):
104     discard
105   imgui.separator()
106   if imgui.menuItem("Select All", "Ctrl+A"):
107     discard
108   if imgui.menuItem("Invert Selection"):
109     discard
110   imgui.separator()
111   if imgui.menuItem("Snap to Grid", "Ctrl+Q"):
112     discard
113   if imgui.menuItem("Bring to Front", "Ctrl+]"):
114     discard
115   if imgui.menuItem("Send to Back", "Ctrl+["):
116     discard
118 proc emitMainViewMenu() =
119   imgui.doMenu("Layers"):
120     for i, x in config.showLayer.mpairs():
121       imgui.menuItem($i, $(i.int + 1), x.addr)
122   imgui.separator()
123   imgui.menuItem("Show Map edges", "", config.showMapEdges.addr)
124   imgui.menuItem("Show Grid", "", config.showGrid.addr)
125   imgui.menuItem("Show Mini Map", "", config.showMiniMap.addr)
126   imgui.separator()
127   imgui.menuItem("Preview Mode", "TAB", config.showPreview.addr)
129 proc emitMainPropertiesMenu() =
130   if imgui.menuItem("Switch Grid step", "Ctrl+E"):
131     discard
132   imgui.separator()
133   if imgui.menuItem("Map Properties"):
134     discard
135   imgui.separator()
136   if imgui.menuItem("Editor Settings"):
137     discard
139 proc emitMainToolsMenu() =
140   if imgui.menuItem("Check Map"):
141     discard
142   if imgui.menuItem("Optimize Map"):
143     discard
144   if imgui.menuItem("Run Map"):
145     discard
147 proc emitAboutWindow() =
148   discard
150 proc emitMainMenuBar() =
151   imgui.doMainMenuBar():
152     imgui.doMenu("File"):
153       emitMainFileMenu()
154     imgui.doMenu("Edit"):
155       emitMainEditMenu()
156     imgui.doMenu("View"):
157       emitMainViewMenu()
158     imgui.doMenu("Properties"):
159       emitMainPropertiesMenu()
160     imgui.doMenu("Tools"):
161       emitMainToolsMenu()
162     imgui.doMenu("Help"):
163       if imgui.menuItem("About"):
164         emitAboutWindow()
166 proc emitSideBar() =
167   imgui.setNextWindowPos(ImVec2(x: float32(gfx.vidWidth() - dockSizeRight), y: menuBarHeight.float32))
168   imgui.setNextWindowSize(ImVec2(x: dockSizeRight.float32, y: float32(gfx.vidHeight() - menuBarHeight)))
169   if imgui.beginWindow("##ObjInfo", nil, ImGuiWindowFlags.NoTitleBar or ImGuiWindowFlags.NoMove or ImGuiWindowFlags.NoResize):
170     imgui.endWindow()
172 proc emitBottomBar() =
173   imgui.setNextWindowPos(ImVec2(x: 0f, y: float32(gfx.vidHeight() - dockSizeBottom)))
174   imgui.setNextWindowSize(ImVec2(x: (gfx.vidWidth() - dockSizeRight).float32, y: dockSizeBottom.float32))
175   if imgui.beginWindow("##BottomBar", nil, ImGuiWindowFlags.NoTitleBar or ImGuiWindowFlags.NoMove or ImGuiWindowFlags.NoResize):
176     imgui.endWindow()
178 proc mainWindow*(): bool =
179   result = true
181   discard defaultInfoBox.display()
182   discard defaultWarningBox.display()
183   discard defaultErrorBox.display()
185   emitMainMenuBar()
186   emitSideBar()
187   emitBottomBar()
189   if confirmBox.display():
190     if confirmBox.getResult() == 1:
191       if confirmAction != nil:
192         confirmAction()
193         confirmAction = nil
194       else:
195         result = false
197   if defaultFileDialog.display():
198     let wadPath = defaultFileDialog.getResultPath()
199     if wadPath != "":
200       if defaultFileDialog.isReading():
201         map.close()
202         try:
203           var ar = vfs.mount(wadPath, true, true, true)
204           if ar != nil:
205             defaultMapDialog.open("Select map", ar, false)
206         except IOError:
207           discard defaultErrorBox.open(mbError, getCurrentExceptionMsg())
209   if defaultMapDialog.display():
210     let mapName = defaultMapDialog.getResult()
211     if mapName != "":
212       if defaultMapDialog.isReading():
213         map.close()
214         map.select(mapName)
216 proc maxViewportSize*(): DFSize =
217   result = gfx.vidSize()
218   result.w -= dockSizeLeft + dockSizeRight + customBarWidth
219   result.h -= dockSizeBottom + customBarWidth + menuBarHeight
221 proc viewportPos*(): DFPoint =
222   result = (dockSizeLeft, menuBarHeight)
224 proc viewportMousePos*(): DFPoint =
225   let v = imgui.getMousePos()
226   if v.x > -1e10f:
227     result.x = v.x.int
228   if v.y > -1e10f:
229     result.y = v.y.int
230   result = result - viewportPos()
232 proc scrollControls(self: var CustomScrollBar, x: var int) =
233   let pos = viewportMousePos()
235   if not imgui.isAnyModalPopupOpen():
236     if self.active:
237       if not imgui.isMouseDown(ImGuiMouseButton.Left):
238         self.active = false
239     else:
240       if imgui.igIsMouseClicked(ImGuiMouseButton.Left):    
241         if intersect(pos, self.start, self.size):
242           self.active = true
243   else:
244     self.active = false
246   if self.active:
247     let mxPos = (if self.axis == 0: pos.x else: pos.y).float32
248     let endX = (self.maxSize - self.size.component(self.axis)).float32
249     let mxStart = self.barSize.component(self.axis).float32 / 2f
250     let mxEnd = self.size.component(self.axis).float32 - mxStart
251     let mxRatio = (mxPos - mxStart) / (mxEnd - mxStart)
252     x = int(clamp(mxRatio, 0f, 1f) * endX)
254 proc hscroll*(self: var CustomScrollBar, x: var int, size, maxSize, startY: int) =
255   self.axis = 0
256   self.start = (0, startY)
257   self.size = (size, customBarWidth)
258   self.barSize = (int((size.float32 / maxSize.float32) * size.float32), customBarWidth)
259   self.barStart = (int((x.float32 / maxSize.float32) * size.float32), self.start.y)
260   self.maxSize = maxSize
262   self.scrollControls(x)
264   gfx.drawQuad(self.start, (self.size.x + customBarWidth, self.size.y), customBarColBg)
265   gfx.drawQuad(self.barStart, self.barSize, (if self.active: customBarColMove else: customBarColFg))
267 proc vscroll*(self: var CustomScrollBar, x: var int, size, maxSize, startX: int) =
268   self.axis = 1
269   self.start = (startX, 0)
270   self.size = (customBarWidth, size)
271   self.barSize = (customBarWidth, int((size.float32 / maxSize.float32) * size.float32))
272   self.barStart = (self.start.x, int((x.float32 / maxSize.float32) * size.float32))
273   self.maxSize = maxSize
275   self.scrollControls(x)
277   gfx.drawQuad(self.start, (self.size.x, self.size.y + customBarWidth), customBarColBg)
278   gfx.drawQuad(self.barStart, self.barSize, (if self.active: customBarColMove else: customBarColFg))