1#include "editor.h"
2#include "enums.h"
3
4#include <engine/font_icons.h>
5
6#include <game/editor/mapitems/image.h>
7#include <game/editor/mapitems/sound.h>
8
9int CEditor::DoProperties(CUIRect *pToolbox, CProperty *pProps, int *pIds, int *pNewVal, const std::vector<ColorRGBA> &vColors)
10{
11 auto Res = DoPropertiesWithState<int>(pToolBox: pToolbox, pProps, pIds, pNewVal, vColors);
12 return Res.m_Value;
13}
14
15template<typename E>
16SEditResult<E> CEditor::DoPropertiesWithState(CUIRect *pToolBox, CProperty *pProps, int *pIds, int *pNewVal, const std::vector<ColorRGBA> &vColors)
17{
18 int Change = -1;
19 EEditState State = EEditState::NONE;
20
21 for(int i = 0; pProps[i].m_pName; i++)
22 {
23 const ColorRGBA *pColor = i >= (int)vColors.size() ? &ms_DefaultPropColor : &vColors[i];
24
25 CUIRect Slot;
26 pToolBox->HSplitTop(Cut: 13.0f, pTop: &Slot, pBottom: pToolBox);
27 CUIRect Label, Shifter;
28 Slot.VSplitMid(pLeft: &Label, pRight: &Shifter);
29 Shifter.HMargin(Cut: 1.0f, pOtherRect: &Shifter);
30 Ui()->DoLabel(pRect: &Label, pText: pProps[i].m_pName, Size: 10.0f, Align: TEXTALIGN_ML);
31
32 if(pProps[i].m_Type == PROPTYPE_INT)
33 {
34 CUIRect Inc, Dec;
35
36 Shifter.VSplitRight(Cut: 10.0f, pLeft: &Shifter, pRight: &Inc);
37 Shifter.VSplitLeft(Cut: 10.0f, pLeft: &Dec, pRight: &Shifter);
38 auto NewValueRes = UiDoValueSelector(pId: (char *)&pIds[i], pRect: &Shifter, pLabel: "", Current: pProps[i].m_Value, Min: pProps[i].m_Min, Max: pProps[i].m_Max, Step: 1, Scale: 1.0f, pToolTip: "Use left mouse button to drag and change the value. Hold shift to be more precise. Right click to edit as text.", IsDegree: false, IsHex: false, Corners: 0, pColor);
39 if(NewValueRes.m_Value != pProps[i].m_Value || (NewValueRes.m_State != EEditState::NONE && NewValueRes.m_State != EEditState::EDITING))
40 {
41 *pNewVal = NewValueRes.m_Value;
42 if(NewValueRes.m_State != EEditState::NONE)
43 {
44 Change = i;
45 }
46 State = NewValueRes.m_State;
47 }
48 if(DoButton_FontIcon(pId: (char *)&pIds[i] + 1, pText: FontIcon::MINUS, Checked: 0, pRect: &Dec, Flags: BUTTONFLAG_LEFT, pToolTip: "Decrease value.", Corners: IGraphics::CORNER_L, FontSize: 7.0f))
49 {
50 *pNewVal = std::clamp(val: pProps[i].m_Value - 1, lo: pProps[i].m_Min, hi: pProps[i].m_Max);
51 Change = i;
52 State = EEditState::ONE_GO;
53 }
54 if(DoButton_FontIcon(pId: ((char *)&pIds[i]) + 2, pText: FontIcon::PLUS, Checked: 0, pRect: &Inc, Flags: BUTTONFLAG_LEFT, pToolTip: "Increase value.", Corners: IGraphics::CORNER_R, FontSize: 7.0f))
55 {
56 *pNewVal = std::clamp(val: pProps[i].m_Value + 1, lo: pProps[i].m_Min, hi: pProps[i].m_Max);
57 Change = i;
58 State = EEditState::ONE_GO;
59 }
60 }
61 else if(pProps[i].m_Type == PROPTYPE_BOOL)
62 {
63 CUIRect No, Yes;
64 Shifter.VSplitMid(pLeft: &No, pRight: &Yes);
65 if(DoButton_Ex(pId: &pIds[i], pText: "No", Checked: !pProps[i].m_Value, pRect: &No, Flags: BUTTONFLAG_LEFT, pToolTip: nullptr, Corners: IGraphics::CORNER_L))
66 {
67 *pNewVal = 0;
68 Change = i;
69 State = EEditState::ONE_GO;
70 }
71 if(DoButton_Ex(pId: ((char *)&pIds[i]) + 1, pText: "Yes", Checked: pProps[i].m_Value, pRect: &Yes, Flags: BUTTONFLAG_LEFT, pToolTip: nullptr, Corners: IGraphics::CORNER_R))
72 {
73 *pNewVal = 1;
74 Change = i;
75 State = EEditState::ONE_GO;
76 }
77 }
78 else if(pProps[i].m_Type == PROPTYPE_ANGLE_SCROLL)
79 {
80 CUIRect Inc, Dec;
81 Shifter.VSplitRight(Cut: 10.0f, pLeft: &Shifter, pRight: &Inc);
82 Shifter.VSplitLeft(Cut: 10.0f, pLeft: &Dec, pRight: &Shifter);
83 const bool Shift = Input()->ShiftIsPressed();
84 int Step = Shift ? 1 : 45;
85
86 auto NewValueRes = UiDoValueSelector(pId: &pIds[i], pRect: &Shifter, pLabel: "", Current: pProps[i].m_Value, Min: pProps[i].m_Min, Max: pProps[i].m_Max, Step: Shift ? 1 : 45, Scale: Shift ? 1.0f : 10.0f, pToolTip: "Use left mouse button to drag and change the value. Hold shift to be more precise. Right click to edit as text.", IsDegree: false, IsHex: false, Corners: 0);
87 if(NewValueRes.m_Value != pProps[i].m_Value || (NewValueRes.m_State != EEditState::NONE && NewValueRes.m_State != EEditState::EDITING))
88 {
89 *pNewVal = NewValueRes.m_Value;
90 if(NewValueRes.m_State != EEditState::NONE)
91 {
92 Change = i;
93 }
94 State = NewValueRes.m_State;
95 }
96 if(DoButton_FontIcon(pId: &pIds[i] + 1, pText: FontIcon::MINUS, Checked: 0, pRect: &Dec, Flags: BUTTONFLAG_LEFT, pToolTip: "Decrease value.", Corners: IGraphics::CORNER_L, FontSize: 7.0f))
97 {
98 *pNewVal = (std::ceil(x: (pProps[i].m_Value / (float)Step)) - 1) * Step;
99 if(*pNewVal < 0)
100 *pNewVal += 360;
101 Change = i;
102 State = EEditState::ONE_GO;
103 }
104 if(DoButton_FontIcon(pId: &pIds[i] + 2, pText: FontIcon::PLUS, Checked: 0, pRect: &Inc, Flags: BUTTONFLAG_LEFT, pToolTip: "Increase value.", Corners: IGraphics::CORNER_R, FontSize: 7.0f))
105 {
106 *pNewVal = (pProps[i].m_Value + Step) / Step * Step % 360;
107 Change = i;
108 State = EEditState::ONE_GO;
109 }
110 }
111 else if(pProps[i].m_Type == PROPTYPE_COLOR)
112 {
113 const auto &&SetColor = [&](ColorRGBA NewColor) {
114 const int NewValue = NewColor.PackAlphaLast();
115 if(NewValue != pProps[i].m_Value || m_ColorPickerPopupContext.m_State != EEditState::EDITING)
116 {
117 *pNewVal = NewValue;
118 if(m_ColorPickerPopupContext.m_State != EEditState::NONE)
119 {
120 Change = i;
121 }
122 State = m_ColorPickerPopupContext.m_State;
123 }
124 };
125 DoColorPickerButton(pId: &pIds[i], pRect: &Shifter, Color: ColorRGBA::UnpackAlphaLast<ColorRGBA>(Color: pProps[i].m_Value), SetColor);
126 }
127 else if(pProps[i].m_Type == PROPTYPE_IMAGE)
128 {
129 const char *pName;
130 if(pProps[i].m_Value < 0)
131 pName = "None";
132 else
133 pName = Map()->m_vpImages[pProps[i].m_Value]->m_aName;
134
135 if(DoButton_Ex(pId: &pIds[i], pText: pName, Checked: 0, pRect: &Shifter, Flags: BUTTONFLAG_LEFT, pToolTip: nullptr, Corners: IGraphics::CORNER_ALL))
136 PopupSelectImageInvoke(Current: pProps[i].m_Value, x: Ui()->MouseX(), y: Ui()->MouseY());
137
138 int Result = PopupSelectImageResult();
139 if(Result >= -1)
140 {
141 *pNewVal = Result;
142 Change = i;
143 State = EEditState::ONE_GO;
144 }
145 }
146 else if(pProps[i].m_Type == PROPTYPE_SHIFT)
147 {
148 CUIRect Left, Right, Up, Down;
149 Shifter.VSplitMid(pLeft: &Left, pRight: &Up, Spacing: 2.0f);
150 Left.VSplitLeft(Cut: 10.0f, pLeft: &Left, pRight: &Shifter);
151 Shifter.VSplitRight(Cut: 10.0f, pLeft: &Shifter, pRight: &Right);
152 Shifter.Draw(Color: ColorRGBA(1, 1, 1, 0.5f), Corners: IGraphics::CORNER_NONE, Rounding: 0.0f);
153 Ui()->DoLabel(pRect: &Shifter, pText: "X", Size: 10.0f, Align: TEXTALIGN_MC);
154 Up.VSplitLeft(Cut: 10.0f, pLeft: &Up, pRight: &Shifter);
155 Shifter.VSplitRight(Cut: 10.0f, pLeft: &Shifter, pRight: &Down);
156 Shifter.Draw(Color: ColorRGBA(1, 1, 1, 0.5f), Corners: IGraphics::CORNER_NONE, Rounding: 0.0f);
157 Ui()->DoLabel(pRect: &Shifter, pText: "Y", Size: 10.0f, Align: TEXTALIGN_MC);
158 if(DoButton_FontIcon(pId: &pIds[i], pText: FontIcon::MINUS, Checked: 0, pRect: &Left, Flags: BUTTONFLAG_LEFT, pToolTip: "Shift left.", Corners: IGraphics::CORNER_L, FontSize: 7.0f))
159 {
160 *pNewVal = (int)EShiftDirection::LEFT;
161 Change = i;
162 State = EEditState::ONE_GO;
163 }
164 if(DoButton_FontIcon(pId: ((char *)&pIds[i]) + 3, pText: FontIcon::PLUS, Checked: 0, pRect: &Right, Flags: BUTTONFLAG_LEFT, pToolTip: "Shift right.", Corners: IGraphics::CORNER_R, FontSize: 7.0f))
165 {
166 *pNewVal = (int)EShiftDirection::RIGHT;
167 Change = i;
168 State = EEditState::ONE_GO;
169 }
170 if(DoButton_FontIcon(pId: ((char *)&pIds[i]) + 1, pText: FontIcon::MINUS, Checked: 0, pRect: &Up, Flags: BUTTONFLAG_LEFT, pToolTip: "Shift up.", Corners: IGraphics::CORNER_L, FontSize: 7.0f))
171 {
172 *pNewVal = (int)EShiftDirection::UP;
173 Change = i;
174 State = EEditState::ONE_GO;
175 }
176 if(DoButton_FontIcon(pId: ((char *)&pIds[i]) + 2, pText: FontIcon::PLUS, Checked: 0, pRect: &Down, Flags: BUTTONFLAG_LEFT, pToolTip: "Shift down.", Corners: IGraphics::CORNER_R, FontSize: 7.0f))
177 {
178 *pNewVal = (int)EShiftDirection::DOWN;
179 Change = i;
180 State = EEditState::ONE_GO;
181 }
182 }
183 else if(pProps[i].m_Type == PROPTYPE_SOUND)
184 {
185 const char *pName;
186 if(pProps[i].m_Value < 0)
187 pName = "None";
188 else
189 pName = Map()->m_vpSounds[pProps[i].m_Value]->m_aName;
190
191 if(DoButton_Ex(pId: &pIds[i], pText: pName, Checked: 0, pRect: &Shifter, Flags: BUTTONFLAG_LEFT, pToolTip: nullptr, Corners: IGraphics::CORNER_ALL))
192 PopupSelectSoundInvoke(Current: pProps[i].m_Value, x: Ui()->MouseX(), y: Ui()->MouseY());
193
194 int Result = PopupSelectSoundResult();
195 if(Result >= -1)
196 {
197 *pNewVal = Result;
198 Change = i;
199 State = EEditState::ONE_GO;
200 }
201 }
202 else if(pProps[i].m_Type == PROPTYPE_AUTOMAPPER)
203 {
204 const char *pName;
205 if(pProps[i].m_Value < 0 || pProps[i].m_Min < 0 || pProps[i].m_Min >= (int)Map()->m_vpImages.size())
206 pName = "None";
207 else
208 pName = Map()->m_vpImages[pProps[i].m_Min]->m_AutoMapper.GetConfigName(Index: pProps[i].m_Value);
209
210 if(DoButton_Ex(pId: &pIds[i], pText: pName, Checked: 0, pRect: &Shifter, Flags: BUTTONFLAG_LEFT, pToolTip: nullptr, Corners: IGraphics::CORNER_ALL))
211 PopupSelectConfigAutoMapInvoke(Current: pProps[i].m_Value, x: Ui()->MouseX(), y: Ui()->MouseY());
212
213 int Result = PopupSelectConfigAutoMapResult();
214 if(Result >= -1)
215 {
216 *pNewVal = Result;
217 Change = i;
218 State = EEditState::ONE_GO;
219 }
220 }
221 else if(pProps[i].m_Type == PROPTYPE_AUTOMAPPER_REFERENCE)
222 {
223 const char *pName;
224 if(pProps[i].m_Value < 0)
225 pName = "None";
226 else
227 pName = AUTOMAP_REFERENCE_NAMES[pProps[i].m_Value];
228
229 if(DoButton_Ex(pId: &pIds[i], pText: pName, Checked: 0, pRect: &Shifter, Flags: BUTTONFLAG_LEFT, pToolTip: nullptr, Corners: IGraphics::CORNER_ALL))
230 PopupSelectAutoMapReferenceInvoke(Current: pProps[i].m_Value, x: Ui()->MouseX(), y: Ui()->MouseY());
231
232 const int Result = PopupSelectAutoMapReferenceResult();
233 if(Result >= -1)
234 {
235 *pNewVal = Result;
236 Change = i;
237 State = EEditState::ONE_GO;
238 }
239 }
240 else if(pProps[i].m_Type == PROPTYPE_ENVELOPE)
241 {
242 CUIRect Inc, Dec;
243 char aBuf[8];
244
245 Shifter.VSplitRight(Cut: 10.0f, pLeft: &Shifter, pRight: &Inc);
246 Shifter.VSplitLeft(Cut: 10.0f, pLeft: &Dec, pRight: &Shifter);
247
248 if(pProps[i].m_Value <= 0 || pProps[i].m_Value > (int)Map()->m_vpEnvelopes.size())
249 {
250 str_copy(dst&: aBuf, src: "None:");
251 }
252 else if(Map()->m_vpEnvelopes[pProps[i].m_Value - 1]->m_aName[0])
253 {
254 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s:", Map()->m_vpEnvelopes[pProps[i].m_Value - 1]->m_aName);
255 if(!str_endswith(str: aBuf, suffix: ":"))
256 {
257 aBuf[sizeof(aBuf) - 2] = ':';
258 aBuf[sizeof(aBuf) - 1] = '\0';
259 }
260 }
261 else
262 {
263 aBuf[0] = '\0';
264 }
265
266 auto NewValueRes = UiDoValueSelector(pId: (char *)&pIds[i], pRect: &Shifter, pLabel: aBuf, Current: pProps[i].m_Value, Min: 0, Max: Map()->m_vpEnvelopes.size(), Step: 1, Scale: 1.0f, pToolTip: "Select envelope.", IsDegree: false, IsHex: false, Corners: IGraphics::CORNER_NONE);
267 if(NewValueRes.m_Value != pProps[i].m_Value || (NewValueRes.m_State != EEditState::NONE && NewValueRes.m_State != EEditState::EDITING))
268 {
269 *pNewVal = NewValueRes.m_Value;
270 if(NewValueRes.m_State != EEditState::NONE)
271 {
272 Change = i;
273 }
274 State = NewValueRes.m_State;
275 }
276 if(DoButton_FontIcon(pId: (char *)&pIds[i] + 1, pText: FontIcon::MINUS, Checked: 0, pRect: &Dec, Flags: BUTTONFLAG_LEFT, pToolTip: "Select previous envelope.", Corners: IGraphics::CORNER_L, FontSize: 7.0f))
277 {
278 *pNewVal = pProps[i].m_Value - 1;
279 Change = i;
280 State = EEditState::ONE_GO;
281 }
282 if(DoButton_FontIcon(pId: ((char *)&pIds[i]) + 2, pText: FontIcon::PLUS, Checked: 0, pRect: &Inc, Flags: BUTTONFLAG_LEFT, pToolTip: "Select next envelope.", Corners: IGraphics::CORNER_R, FontSize: 7.0f))
283 {
284 *pNewVal = pProps[i].m_Value + 1;
285 Change = i;
286 State = EEditState::ONE_GO;
287 }
288 }
289 }
290
291 return SEditResult<E>{State, static_cast<E>(Change)};
292}
293
294template SEditResult<ECircleShapeProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
295template SEditResult<ERectangleShapeProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
296template SEditResult<EGroupProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
297template SEditResult<ELayerProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
298template SEditResult<ELayerQuadsProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
299template SEditResult<ETilesProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
300template SEditResult<ETilesCommonProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
301template SEditResult<ELayerSoundsProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
302template SEditResult<EQuadProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
303template SEditResult<EQuadPointProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
304template SEditResult<ESoundProp> CEditor::DoPropertiesWithState(CUIRect *, CProperty *, int *, int *, const std::vector<ColorRGBA> &);
305