Initial Git commit
[fractals] / GradientDesigner.ec
1 #ifdef ECERE_STATIC
2 import static "ecere"
3 #else
4 import "ecere"
5 #endif
6
7 /*
8 static ColorKey defaultKeys[] =
9
10    { navy, 0.0f },
11    { green, 0.1f },
12    { yellow, 0.2f },
13    { orange, 0.3f },
14    { red, 0.4f },
15    { purple, 0.5f },
16    { ivory, 0.6f },
17    { tomato, 0.7f },
18    { white, 0.8f },
19    { powderBlue, 0.9f },
20    { black, 1 }
21 };
22 */
23 /*
24 static ColorKey defaultKeys[] =
25
26    { navy, 0.0f },
27    { Color { 146, 237, 237 }, 0.2f },
28    { white, 0.3f },
29    { lightYellow, 0.5f },
30    { Color { 255, 100, 0 }, 0.8f },
31    { navy, 0.99f },
32    { black, 1 }
33 };
34 */
35 static ColorKey defaultKeys[] =
36 {
37    { navy, 0.0f },
38    { Color { 146, 213, 237 }, 0.198606268f },
39    { white, 0.3f },
40    { Color { 255, 255, 124 }, 0.444250882f },
41    { Color { 255, 100, 0 }, 0.634146333f },
42    { navy, 1 }
43 };
44
45 class ArrayColorKeys : OldArray
46 {
47    type = class(ColorKey);
48    ColorKey * _;
49 };
50
51 class ColorGradient
52 {
53    ArrayColorKeys keys { };
54    float smoothness;
55    smoothness = 1;
56
57    ColorGradient()
58    {
59       keys.size = sizeof(defaultKeys) / sizeof(ColorKey);
60       memcpy(keys._, defaultKeys, sizeof(ColorKey) * keys.size);
61    }
62 }
63
64 define handleWidth = 15;
65
66 static uint CompareKeys(ColorKey a, ColorKey b)
67 {
68    if(a.percent > b.percent) return 1;
69    else if(a.percent < b.percent) return -1;
70    else return 0;
71 }
72
73 class KeyHandle : Window
74 {
75    size = { handleWidth, 20 };
76    borderStyle = deep;
77
78    bool sliding;
79    float percent;
80    int startPos, startX;
81
82    bool OnLeftButtonDown(int x, int y, Modifiers mods)
83    {
84       GradientDesigner designer = (GradientDesigner)master;
85       sliding = true;
86       startPos = position.x;
87       startX = x;
88
89       Capture();
90       
91       return true;
92    }
93
94    bool OnMouseMove(int x, int y, Modifiers mods)
95    {
96       if(sliding)
97       {
98          GradientDesigner designer = (GradientDesigner)master;
99          int c;
100          ColorKey * key;
101
102          for(c = 0; c < designer.gradient.keys.size; c++)
103          {
104             key = &designer.gradient.keys._[c];
105             if(key->percent == percent && key->color == background)
106                break;
107          }
108
109          x -= startX;
110          x += startPos;
111          x -= designer.preview.position.x;
112          x += handleWidth / 2;
113          key->percent = (float) x / (float) designer.preview.size.w;
114
115          x = Max(x, 0);
116          x = Min(x, designer.preview.size.w);
117          percent = (float) x / (float) designer.preview.size.w;
118
119          position.x = (percent * designer.preview.size.w) + designer.preview.position.x - handleWidth/2;
120          startPos = position.x;
121
122          qsort(designer.gradient.keys._, designer.gradient.keys.size, sizeof(ColorKey), CompareKeys);
123
124          for(c = 0; c < designer.gradient.keys.size; c++)
125          {
126             key = &designer.gradient.keys._[c];
127             if(key->percent < 0) key->percent = 0;
128             else if(key->percent > 1) key->percent = 1;
129          }
130          
131          designer.Update(null);
132          designer.NotifyUpdate(designer.master);
133       }
134       return true;
135    }
136
137    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
138    {
139       ColorPicker colorPicker { master = this };
140       colorPicker.color = background;
141       incref colorPicker;
142       if(colorPicker.Modal() == ok)
143       {
144          GradientDesigner designer = (GradientDesigner)master;
145          int c;
146          ColorKey * key;
147
148          for(c = 0; c < designer.gradient.keys.size; c++)
149          {
150             key = &designer.gradient.keys._[c];
151             if(key->percent == percent && key->color == background)
152                break;
153          }
154          background = colorPicker.color;
155          key->color = background;
156          designer.Update(null);
157          designer.NotifyUpdate(designer.master);
158       }
159       delete colorPicker;
160       return false;
161    }
162
163    bool OnKeyDown(Key key, unichar ch)
164    {
165       if(key == del)
166       {
167          GradientDesigner designer = (GradientDesigner)master;
168          ColorKey * key;
169          int c;
170
171          for(c = 0; c < designer.gradient.keys.size; c++)
172          {
173             key = &designer.gradient.keys._[c];
174             if(key->percent == percent && key->color == background)
175                break;
176          }
177          *key = designer.gradient.keys._[designer.gradient.keys.size-1];
178          designer.gradient.keys.size--;
179          qsort(designer.gradient.keys._, designer.gradient.keys.size, sizeof(ColorKey), CompareKeys);
180          Destroy(0);                  
181
182          designer.Update(null);
183          designer.NotifyUpdate(designer.master);
184          return false;
185       }
186       return true;
187    }
188
189    bool OnLeftButtonUp(int x, int y, Modifiers mods)
190    {
191       if(sliding)
192       {
193          ReleaseCapture();
194          sliding = false;
195       }
196       return true;
197    }
198
199    void OnRedraw(Surface surface)
200    {
201       if(active)
202       {
203          if(background.r > 128 || background.g > 128)
204             surface.SetForeground(darkGray);
205          else
206             surface.SetForeground(white);
207
208          surface.Rectangle(0,0,clientSize.w-1, clientSize.h-1);
209          surface.Rectangle(1,1,clientSize.w-2, clientSize.h-2);
210       }
211    }
212
213    bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
214    {
215       Update(null);
216       return true;
217    }
218 }
219
220 class GradientDesigner : Window
221 {
222    text = "Gradient Designer";
223    borderStyle = fixed;
224    size = { 600, 300 };
225    tabCycle = true;
226
227    ColorGradient gradient { };
228    Window preview
229    {
230       this, anchor = { left = 10, right = 10, top = 75, bottom = 75 };
231       borderStyle = deep;
232
233       void OnRedraw(Surface surface)
234       {
235          ColorGradient gradient = ((GradientDesigner)master).gradient;
236          surface.Gradient(gradient.keys._, gradient.keys.size, gradient.smoothness, horizontal, 0,0, clientSize.w-1, clientSize.h-1);
237       }
238    };
239
240    void OnRedraw(Surface surface)
241    {
242       int c;
243       
244       for(c = 0; c < gradient.keys.size; c++)
245       {
246          ColorKey * key = gradient.keys._ + c;
247          int x = key->percent * preview.size.w + preview.position.x;
248          surface.SetForeground(black);
249          surface.DrawLine(x - 6, preview.position.y - 10, x, preview.position.y);
250          surface.DrawLine(x + 6, preview.position.y - 10, x, preview.position.y);
251          surface.SetForeground(gray);
252          surface.DrawLine(x - 5, preview.position.y - 10, x, preview.position.y - 1);
253          surface.DrawLine(x + 5, preview.position.y - 10, x, preview.position.y - 1);
254       }
255    }
256    ScrollBar smoothness
257    {
258       this, borderStyle = deep, clientSize = { 124, 18 }, position = { 16, 240 }, range = 100;
259       text = "Smoothness"; 
260
261       void NotifyScrolling(ScrollBar scrollBar, ScrollBarAction action, int position, Key key)
262       {
263          gradient.smoothness = position / 100.0f;
264          Update(null);
265          NotifyUpdate(master);
266       }
267    };
268    Label lblSmoothness { this, labeledWindow = smoothness, position = { 16, 220 } };
269
270    void UpdateHandles()
271    {
272       Window window, next;
273       int c;
274
275       smoothness.thumbPosition = (int)gradient.smoothness * 100;
276       for(window = firstChild; window; window = next)
277       {
278          next = window.next;
279          if(window._class == class(KeyHandle))
280          {
281             window.Destroy(0);
282          }
283       }
284       for(c = 0; c < gradient.keys.size; c++)
285       {
286          ColorKey * key = gradient.keys._ + c;
287          KeyHandle handle { this, percent = key->percent, position = { key->percent * preview.size.w + preview.position.x - handleWidth/2, preview.position.y - 30 }, background = key->color };
288          handle.Create();
289       }
290    }
291
292    bool OnCreate()
293    {
294       UpdateHandles();
295       return true;
296    }
297    ColorPicker picker { master = this, autoCreate = false, color = white };
298
299    bool OnLeftButtonDown(int x, int y, Modifiers mods)
300    {
301       if(x >= preview.position.x && x < preview.position.x + preview.size.w && y < preview.position.y && y > preview.position.y - 30)
302       {
303          if(picker.Modal() == ok)
304          {
305             ColorKey * key;
306             int c;
307             KeyHandle handle;
308             
309             gradient.keys.size++;
310             key = gradient.keys._ + gradient.keys.size - 1;
311             x -= preview.position.x;
312             x = Max(Min(x, preview.size.w), 0);
313             key->percent = (float) x / (float) preview.size.w;
314             key->color = picker.color;
315
316             handle = KeyHandle { this, percent = key->percent, position = { key->percent * preview.size.w + preview.position.x - handleWidth/2, preview.position.y - 30 }, background = key->color };
317             handle.Create();
318             
319             qsort(gradient.keys._, gradient.keys.size, sizeof(ColorKey), CompareKeys);
320
321             for(c = 0; c < gradient.keys.size; c++)
322             {
323                key = &gradient.keys._[c];
324                if(key->percent == handle.percent && key->color == handle.background)
325                   break;
326             }
327             key->color = picker.color;
328             handle.background = picker.color;
329             Update(null);
330             NotifyUpdate(master);
331          }
332       }
333       return true;
334    }
335
336    virtual void Window::NotifyUpdate();
337 }