File size: 6,841 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
DefineClass.XRollover = {
	__parents = { "InitDone" },
	properties = {
		{ category = "Rollover", id = "RolloverTranslate", editor = "bool", default = true, },
		{ category = "Rollover", id = "RolloverTemplate", editor = "choice", default = "", items = function() return XTemplateCombo("XRolloverWindow") end, },
		{ category = "Rollover", id = "RolloverAnchor", editor = "choice", default = "smart", items = xpopup_anchor_types, },
		{ category = "Rollover", id = "RolloverAnchorId", editor = "text", default = "", },
		{ category = "Rollover", id = "RolloverText", editor = "text", default = "", translate = function(obj) return obj:GetProperty("RolloverTranslate") end, lines = 3, },
		{ category = "Rollover", id = "RolloverDisabledText", editor = "text", default = "", translate = function(obj) return obj:GetProperty("RolloverTranslate") end, lines = 3, },
		{ category = "Rollover", id = "RolloverOffset", editor = "margins", default = box(0, 0, 0, 0), },
	},
}

XGenerateGetSetFuncs(XRollover)

function XRollover:ResolveRolloverAnchor(context, pos)
	if context and context.anchor then return context.anchor end
	local anchor
	local id = self.RolloverAnchorId
	if id ~= "" then
		local node = self
		anchor = node and rawget(node, id)
		if not anchor then
			while true do
				node = node:ResolveId("node")
				if not node then break end
				if id == "node" or node.Id == id then
					anchor = node
					break
				end
				anchor = node and rawget(node, id)
				if anchor then break end
			end
		end
	end
	return anchor and (anchor.interaction_box or anchor.box) or pos and sizebox(pos:x(), pos:y(), 1, 1) or self.interaction_box or self.box
end

function XRollover:OnXTemplateSetProperty(prop_id, old_value)
	-- toggle text properties between Ts and strings when RolloverTranslate is edited
	if prop_id == "RolloverTranslate" then
		self:UpdateLocalizedProperty("RolloverText", self.RolloverTranslate)
		self:UpdateLocalizedProperty("RolloverDisabledText", self.RolloverTranslate)
		ObjModified(self)
	end
end


----- XRolloverWindow

DefineClass.XRolloverWindow = {
	__parents = { "XPopup", "XDrawCache" },
	HandleMouse = false,
	ChildrenHandleMouse = false,
	ZOrder = 1000000,
	RefreshInterval = 1000,
}

function XRolloverWindow:Init(parent, context)
	assert(context.control)
	if context.control then
		self:SetAnchor(context.control:ResolveRolloverAnchor(context))
		self:SetAnchorType(context.RolloverAnchor or context.control:GetRolloverAnchor())
	end
	if self.RefreshInterval then
		self:CreateThread("UpdateRolloverContent", function(self)
			while true do
				Sleep(self.RefreshInterval)
				self:UpdateRolloverContent()
			end
		end, self)
	end
end

function XRolloverWindow:ControlMove(control)
	self:SetAnchor(control:ResolveRolloverAnchor())
	self:InvalidateLayout()
end

function XRolloverWindow:UpdateRolloverContent()
	local content = rawget(self, "idContent")
	if content then
		content:OnContextUpdate(content.context)
	end
end

----- globals

if FirstLoad then
	RolloverWin = false
	RolloverControl = false
	RolloverGamepad = false
end

function XDestroyRolloverWindow(immediate)
	local win, control = RolloverWin, RolloverControl
	RolloverWin = false
	RolloverControl = false
	if win and win.window_state ~= "destroying" then
		Msg("DestroyRolloverWindow", win, control)
		if immediate then
			win:delete()
		else
			win:Close()
		end
	end
end

function XCreateRolloverWindow(control, gamepad, immediate, context)
	XDestroyRolloverWindow(immediate)
	local modal = terminal.desktop:GetModalWindow()
	if control and control:GetRolloverTemplate() ~= "" then
		local T_text = context and context.RolloverText or control:GetRolloverText()
		local T_context = SubContext(control:GetContext(), context)
		if (T_text or "") ~= "" and (not T_text or not IsT(T_text) or _InternalTranslate(T_text, T_context) ~= "") and (not modal or modal == terminal.desktop or control:IsWithin(modal)) then
			RolloverWin = control:CreateRolloverWindow(gamepad, context) or false
			RolloverControl = control
			RolloverGamepad = gamepad or false
			if Platform.ged then
				g_GedApp:UpdateChildrenDarkMode(RolloverWin)
			end
			Msg("CreateRolloverWindow", RolloverWin, control)
			Msg("XWindowRecreated", RolloverWin)
		end
	end
	return RolloverWin
end

function XGetRolloverControl(desktop)
	desktop = desktop or terminal.desktop
	local win = desktop.last_mouse_target or desktop.mouse_capture
	while win and win.window_state ~= "destroying" do
		local T_text = win:GetRolloverText()
		local T_context = win:GetContext()
		if win:GetRolloverTemplate() ~= "" and T_text ~= "" and (not T_text or not IsT(T_text) or _InternalTranslate(T_text, T_context) ~= "") then
			return win
		end
		win = win.parent
	end
end

function XRecreateRolloverWindow(win)
	if RolloverWin and RolloverControl == win and win.window_state ~= "destroying" then
		if XGetRolloverControl() == win then
			XCreateRolloverWindow(win, RolloverGamepad, true)
		end
	end
end

function XUpdateRolloverWindow(win)
	if RolloverWin and RolloverControl == win and win.window_state ~= "destroying" then
		RolloverWin:UpdateRolloverContent()
	end
end

----- Rollover thread
if FirstLoad then
	RolloverEnabled = true
	RolloverLastControl = false
	RolloverCurrentControl = false
end

function SetRolloverEnabled(enabled)
	RolloverEnabled = enabled
	if not enabled then
		XDestroyRolloverWindow(true)
	end
end

function MouseRollover()
	local last_pos = point20
	local timer
	local desktop = terminal.desktop
	local RolloverTime = const.RolloverTime
	local RolloverRefreshDistance = const.RolloverRefreshDistance
	
	while true do
		local pos = desktop.last_mouse_pos or terminal.GetMousePos()
		local ok, rollover_control = procall(XGetRolloverControl, desktop)
		RolloverCurrentControl = ok and rollover_control or false

		-- change rollover shortly
		if RolloverCurrentControl ~= RolloverLastControl
			or (RolloverLastControl and pos:Dist2D(last_pos) > RolloverRefreshDistance)
		then
			timer = timer or (RolloverCurrentControl ~= RolloverLastControl and RolloverTime or 0)
		elseif not RolloverLastControl then
			timer = false
		end

		if timer and timer < RolloverTime - const.RolloverDestroyTime then
			XDestroyRolloverWindow()
		end
		if timer and timer <= 0 and RolloverEnabled then
			XCreateRolloverWindow(RolloverCurrentControl, false)
			RolloverLastControl, last_pos = RolloverCurrentControl, pos
			timer = false
		end

		Sleep(100)
		timer = timer and timer - 100
	end
end

if FirstLoad then
	RolloverThread = false
end
if Platform.desktop then
	DeleteThread(RolloverThread)
	RolloverThread = CreateRealTimeThread(MouseRollover)
end

if Platform.console then
	function OnMsg.MouseConnected()
		DeleteThread(RolloverThread)
		RolloverThread = CreateRealTimeThread(MouseRollover)
	end

	function OnMsg.MouseDisconnected()
		DeleteThread(RolloverThread)
	end
end