File size: 10,442 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
function OpenXWindowInspector(context)
	PauseLuaThreads("XWindowInspector")
	CreateRealTimeThread(function()
		local target = terminal.desktop:GetMouseTarget(terminal.GetMousePos()) or terminal.desktop
		local gedTarget = GetParentOfKind(target, "GedApp")
		if gedTarget then
			context.dark_mode = gedTarget.dark_mode
		end
		local ged = OpenGedApp("XWindowInspector", terminal.desktop, context)
		if ged then
			GedXWindowInspectorSelectWindow(ged, target)
		else
			ResumeLuaThreads("XWindowInspector")
		end
	end)
end

function OnMsg.LuaThreadsPaused()
	ObjModified(terminal.desktop) -- updates the status text, which is bound with context "root" (which happens to be the desktop)
end

function GedThreadsPausedStatus()
	return AreLuaThreadsPaused() and "<style GedError>Lua threads are PAUSED to freeze the UI!" or "Threads currently running..."
end

function GedTogglePauseLuaThreads(ged)
	local was_paused = next(PauseLuaThreadsReasons)
	if was_paused then
		ResumeLuaThreads("XWindowInspector")
		local pause_reason = next(PauseLuaThreadsReasons)
		if pause_reason then
			ged:ShowMessage("Warning", string.format("Lua threads are still paused due to reason %s", pause_reason))
		end
	else
		PauseLuaThreads("XWindowInspector")
	end
end

if FirstLoad then
	GedXWindowInspectors = {}
	GedXWindowInspectorSelection = setmetatable({}, weak_keys_meta)
	GedXWindowInspectorTerminalTarget = false
end

function OnMsg.ClassesGenerate()
	assert(not (XWindow.TreeView or XWindow.PlacementText or XWindow.OnEditorSelect))
	XWindow.TreeView = T(408752573312, "<NodeColor><class> <color 128 128 128><Id><PlacementText>")
	XWindow.NodeColor = function(self)
		return self.IdNode and #self > 0 and "<color 75 105 198>" or ""
	end
	XWindow.PlacementText = function(self)
		local ret = { (self:GetProperty("Id") ~= "") and "" or nil }
		local dbg_template = rawget(self, "__dbg_template_template") or rawget(self, "__dbg_template")
		if dbg_template then
			ret[#ret+1] = "T: " .. dbg_template
		end
		local dock = self:GetProperty("Dock")
		if dock then
			ret[#ret+1] = "Dock: " .. dock
		end
		local dbg_template_comment = rawget(self, "__dbg_template_comment")
		if dbg_template_comment then
			ret[#ret+1] = "<color 0 128 0>" .. dbg_template_comment
		end
		return Untranslated(table.concat(ret, " "))
	end
	XWindow.OnEditorSelect = function(self, selected, ged)
		if selected then
			GedXWindowInspectorSelection[ged] = self
		end
	end
end

local function GedUpdateActionToggled(actionid, value)
	for k, socket in pairs(GedConnections) do
		if socket.app_template == "XWindowInspector" or socket.app_template == "GedParticleEditor" then
			socket:Send("rfnApp", "SetActionToggled", actionid, value)
		end
	end
end

local function GedUpdateInspectorActions(socket)
	socket:Send("rfnApp", "SetActionToggled", "FocusLogging", terminal.desktop.focus_logging_enabled)
	socket:Send("rfnApp", "SetActionToggled", "RolloverLogging", terminal.desktop.rollover_logging_enabled)
	socket:Send("rfnApp", "SetActionToggled", "ContextLogging", XContextUpdateLogging)
	socket:Send("rfnApp", "SetActionToggled", "RolloverMode", GedXWindowInspectorTerminalTarget and GedXWindowInspectorTerminalTarget.enabled)
end

function OnMsg.GedOpened(ged_id)
	local ged = GedConnections[ged_id]
	if ged and ged.app_template == "XWindowInspector" then
		table.insert(GedXWindowInspectors, ged)
	end
	if ged and (ged.app_template == "XWindowInspector" or ged.app_template == "GedParticleEditor") then
		GedUpdateInspectorActions(ged)
	end
end

function GedRpcBindToGlobal(ged, path, global_name)
	local obj = ged:ResolveObj(path)
	rawset(_G, global_name, obj)
end

function OnMsg.GedClosing(ged_id)
	table.remove_entry(GedXWindowInspectors, "ged_id", ged_id)
	if not next(GedXWindowInspectors) then
		ResumeLuaThreads("XWindowInspector")
	end
end

function OnMsg.XWindowModified(win, child, leaving)
	if #GedXWindowInspectors == 0 then return end

	if leaving then
		-- validate selected windows
		for ged, selection in pairs(GedXWindowInspectorSelection) do
			if child == selection then
				GedXWindowInspectorSelection[ged] = win
			end
		end
		
		-- close inspectors with deleted root objects
		for _, inspector in ipairs(GedXWindowInspectors) do
			if child == inspector:ResolveObj("root") then
				inspector:Close()
			end
		end
	end

	repeat
		for _, inspector in ipairs(GedXWindowInspectors) do
			if win == inspector:ResolveObj("root") then
				-- go through a thread, so multiple changes to a XWindow only result in a single call
				if not win:IsThreadRunning("XWindowInspectorObjModified") then
					local ged, modified = inspector, win
					win:CreateThread("XWindowInspectorObjModified", function()
						ObjModified(modified)
						GedXWindowInspectorSelectWindow(ged, GedXWindowInspectorSelection[ged])
					end)
				end
			end
		end
		win = win.parent
	until not win
end

local function GetItemPath(root, control)
	local path = {}
	if not root or not control or not control:IsWithin(root) then
		return path
	end
	local target = control
	while target.parent and target ~= root do
		local idx = table.find(target.parent, target)
		table.insert(path, 1, idx)
		target = target.parent
	end
	return path
end

function GedXWindowInspectorSelectWindow(socket, win)
	local root = socket:ResolveObj("root")
	if socket.selected_object ~= win then
		socket:SetSelection("root", GetItemPath(root, win))
		socket.selected_object = win
	end
end

function GedGetXWindowPath(obj)
	local data = {}
	repeat
		table.insert(data, 1, {
			text = _InternalTranslate(XWindow.TreeView, obj, false),
			path = GetItemPath(terminal.desktop, obj),
		})
		obj = obj.parent
	until not obj
	return data
end

DefineClass.RolloverModeTerminalTarget = {
	__parents = { "TerminalTarget" },
	
	enabled = false,
	callback = false,
	terminal_target_priority = 20000000,
}

function RolloverModeTerminalTarget:MouseEvent(event, pt, button, time)
	if not self.enabled then
		return "continue"
	end
	
	local target = terminal.desktop:GetMouseTarget(pt) or terminal.desktop
	if event == "OnMouseButtonDown" then
		self.enabled = false
		if button == "R" then
			self.callback(target, "cancel")
		else
			self.callback(target, "done")
		end
		GedUpdateActionToggled("RolloverMode", false)
	else
		self.callback(target, "update")
	end
	return "break"
end

function RolloverModeTerminalTarget:EnableRolloverMode(enabled, callback)
	if self.callback and self.enabled then
		self.callback(false, "cancel")
	end
	self.enabled = enabled
	self.callback = callback
	GedUpdateActionToggled("RolloverMode", self.enabled)
end

local flashing_window = false
function XRolloverMode(enabled, callback)
	if not GedXWindowInspectorTerminalTarget then
		GedXWindowInspectorTerminalTarget = RolloverModeTerminalTarget:new()
		terminal.AddTarget(GedXWindowInspectorTerminalTarget)
	end
	GedXWindowInspectorTerminalTarget:EnableRolloverMode(enabled, callback)
end

function GedRpcRolloverMode(socket, enabled)
	local old_sel = socket:ResolveObj("SelectedWindow")
	XRolloverMode(enabled, function(window, status)
		if window then
			if status == "cancel" then
				GedXWindowInspectorSelectWindow(socket, old_sel)
			else
				GedXWindowInspectorSelectWindow(socket, window)
			end
		end
	end)
end

function GedRpcColorPickerRollover(ged, name, prop_id)
	local obj = ged:ResolveObj(name)
	if not obj then return end

	local thread_status = "updating"
	CreateRealTimeThread(function()
		flashing_window = {
			BorderWidth = 2,
			BorderColor = RGB(200, 0, 0),
			Box = terminal.desktop.box,
			Thread = false,
		}
		UIL.Invalidate()

		SetPostProcPredicate("debug_color_pick", true)

		local old_value = obj:GetProperty(prop_id)
		while thread_status == "updating" do
			local pixel = ReturnPixel()
			if pixel and pixel ~= obj:GetProperty(prop_id) then
				obj:SetProperty(prop_id, pixel)
				ObjModified(obj)
			end
			Sleep(10)
		end
		if thread_status == "cancel" then
			obj:SetProperty(prop_id, old_value)
			ObjModified(obj)
		end

		SetPostProcPredicate("debug_color_pick", false)
		
		flashing_window = false
		UIL.Invalidate()
	end)
	XRolloverMode(true, function(window, status)
		if status == "done" then
			thread_status = "done"
		elseif status == "cancel" then
			thread_status = "cancel"
		else			
			local pos = terminal.GetMousePos()
			RequestPixel(pos:x(), pos:y())
		end
	end)
	terminal.BringToTop()
end

function GedRpcInspectFocusedWindow(socket)
	local desktop = terminal.desktop
	local target = desktop:GetKeyboardFocus() or desktop:NextFocusCandidate()
	socket:Send("rfnApp", "SetSelection", "root", target and GetItemPath(socket:ResolveObj("root"), target))
end

function GedRpcToggleFocusLogging(socket, enabled)
	terminal.desktop.focus_logging_enabled = enabled
	GedUpdateActionToggled("FocusLogging", enabled)
end

function GedRpcToggleRolloverLogging(socket, enabled)
	terminal.desktop.rollover_logging_enabled = enabled
	GedUpdateActionToggled("RolloverLogging", enabled)
end

function GedRpcToggleContextLogging(socket, enabled)
	XContextUpdateLogging = enabled
	GedUpdateActionToggled("ContextLogging", enabled)
end

function XFlashWindow(obj)
	if not obj then return end
	if flashing_window then
		DeleteThread(flashing_window.Thread)
	end
	flashing_window = {
		BorderWidth = 1,
		BorderColor = RGB(0, 0, 0),
		Box = box(0, 0, 0, 0),
		Thread = false,
	}
	flashing_window.Thread = CreateRealTimeThread(function()
		for i = 1, 5 do
			local target = obj.interaction_box or obj.box
			if obj.window_state == "destroying" or not target then
				break
			end
			flashing_window.Box = target
			flashing_window.BorderColor = RGB(255, 255, 255)
			UIL.Invalidate()
			Sleep(50)
			flashing_window.BorderColor = RGB(0, 0, 0)
			UIL.Invalidate()
			Sleep(50)
		end
		flashing_window = false
		UIL.Invalidate()
	end)
end

function GedRpcFlashWindow(socket, obj_name)
	local obj = socket:ResolveObj(obj_name)
	XFlashWindow(obj)
end

function GedRpcXWindowInspector(socket, obj_name)
	local obj = socket:ResolveObj(obj_name)
	CreateRealTimeThread(function()
		OpenGedApp("XWindowInspector", obj)
	end)
end

function GedXWindowInspectorFlashWindow()
	if flashing_window then
		local border_width = flashing_window.BorderWidth
		UIL.DrawBorderRect(flashing_window.Box, border_width, border_width, flashing_window.BorderColor, RGBA(0, 0, 0, 0))
	end
end

function OnMsg.Start()
	if Platform.desktop then
		UIL.Register("GedXWindowInspectorFlashWindow", XDesktop.terminal_target_priority + 1)
	end
end