1 /*
2     Copyright © 2020, Inochi2D Project
3     Distributed under the 2-Clause BSD License, see LICENSE file.
4     
5     Authors: Luna Nielsen
6 */
7 module creator.windows.paramsplit;
8 import creator.windows;
9 import creator.core;
10 import creator.widgets.dummy;
11 import creator.widgets.label;
12 import creator;
13 import std.string;
14 import creator.utils.link;
15 import inochi2d;
16 import i18n;
17 import std.array : insertInPlace;
18 
19 struct ParamMapping {
20     size_t idx;
21     ParameterBinding[] bindings;
22     Node node;
23     bool take;
24 }
25 
26 class ParamSplitWindow : Window {
27 private:
28     size_t idx;
29     Parameter param;
30     ParamMapping[uint] mappings;
31 
32     void buildMapping() {
33         foreach(i, ref binding; param.bindings) {
34             if (binding.getNodeUUID() !in mappings) {
35                 mappings[binding.getNodeUUID()] = ParamMapping(
36                     i,
37                     [],
38                     binding.getNode(),
39                     false
40                 );
41             }
42 
43             mappings[binding.getNodeUUID()].bindings ~= binding;
44         }
45     }
46 
47     void apply() {
48         Parameter newParam = new Parameter(param.name~_(" (Split)"), param.isVec2);
49         foreach(axis; 0..param.axisPoints.length) {
50             newParam.axisPoints[axis] = param.axisPoints[axis].dup;
51         }
52         
53 
54         // TODO: remap
55         ParameterBinding[] oldParamBindings;
56         ParameterBinding[] newParamBindings;
57         foreach(ref mappingNode; mappings) {
58             if (!mappingNode.take) oldParamBindings ~= mappingNode.bindings;
59             else newParamBindings ~= mappingNode.bindings;
60         }
61 
62         if (newParamBindings.length > 0) {
63             param.bindings = oldParamBindings;
64             newParam.bindings = newParamBindings;
65             incActivePuppet().parameters.insertInPlace(idx+1, newParam);
66         }
67 
68         this.close();
69     }
70 
71     void oldBindingsList() {
72 
73         foreach(k; 0..mappings.keys.length) {
74             auto key = mappings.keys[k];
75             auto mapping = &mappings[mappings.keys[k]];
76 
77             if (mapping.take) continue;
78 
79             igSelectable(mapping.node.name.toStringz);
80             if(igBeginDragDropSource(ImGuiDragDropFlags.SourceAllowNullID)) {
81                 igSetDragDropPayload("__OLD_TO_NEW", cast(void*)&key, (&key).sizeof, ImGuiCond.Always);
82                 incText(mapping.node.name);
83                 igEndDragDropSource();
84             }
85         }
86     }
87 
88     void newBindingsList() {
89         
90         foreach(k; 0..mappings.keys.length) {
91             auto key = mappings.keys[k];
92             auto mapping = &mappings[mappings.keys[k]];
93             if (!mapping.take) continue;
94             
95             igSelectable(mapping.node.name.toStringz);
96             if(igBeginDragDropSource(ImGuiDragDropFlags.SourceAllowNullID)) {
97                 igSetDragDropPayload("__NEW_TO_OLD", cast(void*)&key, (&key).sizeof, ImGuiCond.Always);
98                 incText(mapping.node.name);
99                 igEndDragDropSource();
100             }
101         }
102     }
103 
104 protected:
105 
106     override
107     void onBeginUpdate() {
108         igSetNextWindowSizeConstraints(ImVec2(640, 480), ImVec2(float.max, float.max));
109         super.onBeginUpdate();
110     }
111 
112     override
113     void onUpdate() {
114         ImVec2 space = incAvailableSpace();
115         float gapspace = 8;
116         float childWidth = (space.x/2);
117         float childHeight = space.y-(24);
118 
119         igBeginGroup();
120             if (igBeginChild("###OldParam", ImVec2(childWidth, childHeight))) {
121                 if (igBeginListBox("###ItemListOld", ImVec2(childWidth-gapspace, childHeight))) {
122                     oldBindingsList();
123                     igEndListBox();
124                 }
125                 
126                 if(igBeginDragDropTarget()) {
127                     const(ImGuiPayload)* payload = igAcceptDragDropPayload("__NEW_TO_OLD");
128                     if (payload !is null) {
129                         uint mappingName = *cast(uint*)payload.Data;
130                         
131                         mappings[mappingName].take = false;
132 
133                         igEndDragDropTarget();
134                         return;
135                     }
136                     igEndDragDropTarget();
137                 }
138             }
139             igEndChild();
140 
141             igSameLine(0, gapspace);
142 
143             if (igBeginChild("###NewParam", ImVec2(childWidth, childHeight))) {
144                 if (igBeginListBox("###ItemListNew", ImVec2(childWidth, childHeight))) {
145                     newBindingsList();
146                     igEndListBox();
147                 }
148             }
149             igEndChild();
150 
151             if(igBeginDragDropTarget()) {
152                 const(ImGuiPayload)* payload = igAcceptDragDropPayload("__OLD_TO_NEW");
153                 if (payload !is null) {
154                     uint mappingName = *cast(uint*)payload.Data;
155                     
156                     mappings[mappingName].take = true;
157 
158                     igEndDragDropTarget();
159                     return;
160                 }
161                 igEndDragDropTarget();
162             }
163         igEndGroup();
164 
165         igBeginGroup();
166             incDummy(ImVec2(-64, 24));
167             igSameLine(0, 0);
168             if (igButton(__("Apply"), ImVec2(64, 24))) {
169                 this.apply();
170             }
171         igEndGroup();
172     }
173 
174 public:
175     this(size_t idx, Parameter param) {
176         this.idx = idx;
177         this.param = param;
178         this.buildMapping();
179         super(_("Split Parameter"));
180     }
181 }
182