File size: 9,623 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
DefineClass.XFrame = {
	__parents = { "XControl" },
	
	properties = {
		{ category = "Image", id = "Image", name = "Frame image", editor = "ui_image", default = "", invalidate = true, },
		{ category = "Image", id = "ImageScale", name = "Frame scale", editor = "point2d", default = point(1000, 1000), invalidate = true, lock_ratio = true, },
		{ category = "Image", id = "FrameBox", name = "Frame box", editor = "padding", default = box(0, 0, 0, 0), invalidate = true, },
		{ category = "Image", id = "Rows", editor = "number", default = 1, invalidate = true, },
		{ category = "Image", id = "Columns", editor = "number", default = 1, invalidate = true, },
		{ category = "Image", id = "Row", editor = "number", default = 1, invalidate = true, },
		{ category = "Image", id = "Column", editor = "number", default = 1, invalidate = true, },
		{ category = "Image", id = "TileFrame", editor = "bool", default = false, invalidate = true, },
		{ category = "Image", id = "SqueezeX", editor = "bool", default = true, invalidate = true, },
		{ category = "Image", id = "SqueezeY", editor = "bool", default = true, invalidate = true, },
		{ category = "Image", id = "TransparentCenter", editor = "bool", default = false, invalidate = true, },
		{ category = "Image", id = "FlipX", editor = "bool", default = false, invalidate = true, },
		{ category = "Image", id = "FlipY", editor = "bool", default = false, invalidate = true, },
		{ category = "Image", id = "Desaturation", editor = "number", default = 0, min = 0, max = 255, slider = true, invalidate = true, },
	},
	Background = RGB(255, 255, 255),
	FocusedBackground = RGB(255, 255, 255),
	HandleMouse = false,
	image_id = const.InvalidResourceID,
	image_obj = false,
}

function XFrame:Init()
	self:SetImage(self.Image, true)
end

function XFrame:Done()
	if self.image_obj ~= false then
		self.image_obj:ReleaseRef()
		self.image_obj = false
	end
end

local InvalidResourceID = const.InvalidResourceID
function XFrame:SetImage(image, force)
	if self.Image == (image or "") and not force then return end	
	self.Image = image or nil

	self:DeleteThread("LoadImage")
	if (self.Image or "") == "" then
		return
	end
	
	if self.image_obj ~= false then
		self.image_obj:ReleaseRef()
		self.image_obj = false
	end
	
	self.image_id = ResourceManager.GetResourceID(self.Image)
	if self.image_id == InvalidResourceID then
		printf("once", "Could not load image %s!", self.Image or "")
		return
	end
	
	self.image_obj = ResourceManager.GetResource(self.image_id)
	if self.image_obj then
		self:InvalidateMeasure()
		self:Invalidate()
	else
		self:CreateThread("LoadImage", function(self)
			self.image_obj = AsyncGetResource(self.image_id)
			self:InvalidateMeasure()
			self:Invalidate()
		end, self)
	end
end

function XFrame:Measure(preferred_width, preferred_height)
	local width, height = XControl.Measure(self, preferred_width, preferred_height)
	if self.image_id ~= InvalidResourceID and (not self.SqueezeX or not self.SqueezeY) then
		local image_width, image_height = ResourceManager.GetMetadataTextureSizeXY(self.image_id)
		image_width, image_height = ScaleXY(self.scale, ScaleXY(self.ImageScale, (image_width or 0) / self.Columns, (image_height or 0) / self.Rows))
		if not self.SqueezeX then
			width = Max(image_width, width)
		end
		if not self.SqueezeY then
			height = Max(image_height, height)
		end
	end
	return width, height
end

local UIL = UIL
local rgbWhite = RGB(255, 255, 255)
function XFrame:DrawBackground()
	if self.image_id ~= InvalidResourceID then
		local color = self:CalcBackground()
		if GetAlpha(color) == 0 then return end
		local desaturation = UIL.GetDesaturation()
		UIL.SetDesaturation(self.Desaturation)
		UIL.SetColor(color)
		local scaleX, scaleY = ScaleXY(self.scale, self.ImageScale:xy())
		UIL.DrawFrame(self.image_id, self.box, self.Rows, self.Columns, self:GetRow(), self:GetColumn(),
			self.FrameBox, not self.TileFrame, self.TransparentCenter, scaleX, scaleY, self.FlipX, self.FlipY)
		UIL.SetColor(rgbWhite)
		UIL.SetDesaturation(desaturation)
	else
		XControl.DrawBackground(self)
	end
end


----- XFrameProgress

local PushClipRect = UIL.PushClipRect
local PopClipRect = UIL.PopClipRect

DefineClass.XFrameProgress = {
	__parents = { "XFrame", "XProgress" },
	properties = {
		{ category = "Image", id = "ProgressImage", name = "Progress frame image", editor = "ui_image", default = "", invalidate = true, },
		{ category = "Image", id = "ProgressFrameBox", name = "Progress frame box", editor = "padding", default = box(0, 0, 0, 0), invalidate = true, },
		{ category = "Image", id = "ProgressTileFrame", editor = "bool", default = false, invalidate = true, },
		{ category = "Image", id = "SeparatorImage", name = "Separator Image", editor = "ui_image", default = "", invalidate = true, },
		{ category = "Image", id = "SeparatorOffset", name = "Separator Offset", editor = "number", default = 0, invalidate = true, },
	},
	SqueezeY = false,
	separator_x = 0,
	separator_y = 0,
	TimeProgressInt = false,
}

function XFrameProgress:Init(parent, context)
	local progress = XFrame:new({
		Id = "idProgress",
		HAlign = self.ProgressClip and "stretch" or "left",
		VAlign = "center",
		SqueezeY = false,
		DrawBackground = function(self)
			local clip = self.parent.ProgressClip and not self.TimeProgressInt
			if clip then
				local parent = self.parent
				local progress, max_progress = parent.Progress, Max(1, parent.MaxProgress)
				local min = ScaleXY(self.scale, parent.MinProgressSize)
				local min_x, min_y = self.content_box:minxyz()
				local width, height = self.content_box:sizexyz()
				if parent.Horizontal then
					local clip_width = (max_progress == 0) and min or min + (width - min) * progress / max_progress
					PushClipRect(min_x, min_y, min_x + clip_width, min_y + height, true)
				else
					local clip_height = (max_progress == 0) and min or min + (height - min) * progress / max_progress
					PushClipRect(min_x, min_y + height - clip_height, min_x + width, min_y + height, true)
				end
			end
			XFrame.DrawBackground(self)
			if clip then
				PopClipRect()
			end
		end,
		DrawContent = function(self)
			local parent = self.parent
			local image = parent.SeparatorImage
			if image ~= "" then
				local separator_x, separator_y = parent.separator_x, parent.separator_y
				local b = self.box
				local scale_x, scale_y = self.scale:xy()
				local offset = parent.SeparatorOffset * scale_x / 1000
				local rect
				local progressRatio = MulDivRound(parent.Progress, 1000, parent.MaxProgress)
				if parent.Horizontal then
					local w = MulDivRound(b:sizex(), progressRatio, 1000)
					b = sizebox(b:minx(), b:miny(), w, b:sizey())
					local right_spill = offset - ((parent.measure_width - self.measure_width) * scale_x / 1000)
					right_spill = Max(right_spill, 0)
					rect = box(Max(b:minx(), b:maxx() - separator_x - offset) + offset, b:miny(), Max(b:minx() + offset, b:maxx() - right_spill), b:maxy())
				else
					local h = MulDivRound(b:sizey(), progressRatio, 1000)
					b = sizebox(b:minx(), b:miny(), b:sizex(), h)
					local up_spill = offset - (b:miny() * scale_y / 1000)
					up_spill = Max(up_spill, 0)
					rect = box(b:minx(), Min(b:maxy() - offset, b:miny() + up_spill), b:maxx(), Min(b:maxy(), b:miny() + separator_y + offset) - offset)
				end
				UIL.DrawImage(image, rect, box(0, 0, separator_x, separator_y))
			end
		end,
	}, self, context)
	progress:SetImage(self.ProgressImage)
	progress:SetFrameBox(self.ProgressFrameBox)
	progress:SetTileFrame(self.ProgressTileFrame)
end

LinkPropertyToChild(XFrameProgress, "ProgressImage", "idProgress", "Image")
LinkPropertyToChild(XFrameProgress, "ProgressFrameBox", "idProgress", "FrameBox")
LinkPropertyToChild(XFrameProgress, "ProgressTileFrame", "idProgress", "TileFrame")

function XFrameProgress:SetHorizontal(h)
	self.Horizontal = h
	local progress = self.idProgress
	if self.ProgressClip then
		progress:SetHAlign(h and "stretch" or "center")
		progress:SetVAlign(h and "center" or "stretch")
	else
		progress:SetHAlign(h and "left" or "center")
		progress:SetVAlign(h and "center" or "bottom")
	end
	progress:SetSqueezeY(not h)
	progress:SetSqueezeX(h)
	self:SetSqueezeY(not h)
	self:SetSqueezeX(h)
	self:InvalidateMeasure()
end

function XFrameProgress:SetSeparatorImage(image)
	image = image or false
	if self.SeparatorImage ~= image then
		self.SeparatorImage = image
		self.separator_x, self.separator_y = UIL.MeasureImage(image)
		self:Invalidate()
	end
end

function XFrameProgress:OnPropUpdate(context, prop_meta, value)
	if not self.TimeProgressInt then
		XProgress.OnPropUpdate(self, context, prop_meta, value)
	end
end

function XFrameProgress:SetTimeProgress(start_time, end_time, bGameTime)
	local prev = self.TimeProgressInt
	if prev and prev.start == start_time and prev.duration + prev.start == end_time and
		not bGameTime == not IsFlagSet(prev.flags or 0, const.intfGameTime) then
		-- setting the same time progress
		return
	end
	self.idProgress:RemoveModifier(prev)
	self.TimeProgressInt = nil
	if start_time and end_time then
		self.TimeProgressInt = {
			id = "TimeProgressBar",
			type = const.intRect,
			OnLayoutComplete = IntRectTopLeftRelative,
			OnWindowMove = IntRectTopLeftRelative,
			targetRect = sizebox(0, 0, 0, 100),
			originalRect = sizebox(0, 0, 100, 100),
			duration = end_time - start_time,
			start = start_time,
			interpolate_clip = self.UseClipBox and const.interpolateClipOnly or true,
			flags = const.intfInverse + (bGameTime and const.intfGameTime or 0),
		}
		self.idProgress:AddInterpolation(self.TimeProgressInt)
		self:SetMaxProgress(100)
		self:SetProgress(100)
	end
end