summaryrefslogtreecommitdiff
path: root/src/lib/evas/canvas/efl_animation_object_group_sequential.c
blob: d7f717885b302a1ea33d069eebbdc244fcbf46c7 (plain)
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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#include "efl_animation_object_group_sequential_private.h"

/* Add member object data and append the data to the member object data list.
 * The member object data contains the repeated count of the member object.
 */
static void
_member_anim_obj_data_add(Efl_Animation_Object_Group_Sequential_Data *pd,
                          Efl_Animation_Object *anim_obj,
                          int repeated_count)
{
   Member_Object_Data *member_anim_obj_data =
      calloc(1, sizeof(Member_Object_Data));

   if (!member_anim_obj_data) return;

   member_anim_obj_data->anim_obj = anim_obj;
   member_anim_obj_data->repeated_count = repeated_count;

   pd->member_anim_obj_data_list =
      eina_list_append(pd->member_anim_obj_data_list, member_anim_obj_data);
}

/* Find the member object data which contains the repeated count of the member
 * object. */
static Member_Object_Data *
_member_anim_obj_data_find(Efl_Animation_Object_Group_Sequential_Data *pd,
                           Efl_Animation_Object *anim_obj)
{
   Eina_List *l;
   Member_Object_Data *member_anim_obj_data = NULL;
   EINA_LIST_FOREACH(pd->member_anim_obj_data_list, l, member_anim_obj_data)
     {
        if (member_anim_obj_data->anim_obj == anim_obj)
          break;
     }

   return member_anim_obj_data;
}

/* Delete member object data and remove the data from the member object data
 * list.
 * The member object data contains the repeated count of the member object.
 */
static void
_member_anim_obj_data_del(Efl_Animation_Object_Group_Sequential_Data *pd,
                          Efl_Animation_Object *anim_obj)
{
   Member_Object_Data *member_anim_obj_data =
      _member_anim_obj_data_find(pd, anim_obj);
   if (member_anim_obj_data)
     {
        pd->member_anim_obj_data_list =
           eina_list_remove(pd->member_anim_obj_data_list,
                            member_anim_obj_data);
        free(member_anim_obj_data);
     }
}

EOLIAN static void
_efl_animation_object_group_sequential_efl_animation_object_group_object_add(Eo *eo_obj,
                                                                             Efl_Animation_Object_Group_Sequential_Data *pd EINA_UNUSED,
                                                                             Efl_Animation_Object *anim_obj)
{
   if (!anim_obj) return;

   efl_animation_object_group_object_add(efl_super(eo_obj, MY_CLASS), anim_obj);

   /* Add member object data and append the data to the member object data
    * list. */
   _member_anim_obj_data_add(pd, anim_obj, 0);

   /* Total duration is calculated in
    * efl_animation_object_total_duration_get() based on the current group
    * animation object list.
    * Therefore, the calculated total duration should be set to update total
    * duration. */
   double total_duration = efl_animation_object_total_duration_get(eo_obj);
   efl_animation_object_total_duration_set(eo_obj, total_duration);
}

EOLIAN static void
_efl_animation_object_group_sequential_efl_animation_object_group_object_del(Eo *eo_obj,
                                                                             Efl_Animation_Object_Group_Sequential_Data *pd EINA_UNUSED,
                                                                             Efl_Animation_Object *anim_obj)
{
   if (!anim_obj) return;

   efl_animation_object_group_object_del(efl_super(eo_obj, MY_CLASS), anim_obj);

   /* Delete member object data and remove the data from the member object
    * data list. */
   _member_anim_obj_data_del(pd, anim_obj);

   /* Total duration is calculated in
    * efl_animation_object_total_duration_get() based on the current group
    * animation object list.
    * Therefore, the calculated total duration should be set to update total
    * duration. */
   double total_duration = efl_animation_object_total_duration_get(eo_obj);
   efl_animation_object_total_duration_set(eo_obj, total_duration);
}

EOLIAN static double
_efl_animation_object_group_sequential_efl_animation_object_total_duration_get(Eo *eo_obj,
                                                                               Efl_Animation_Object_Group_Sequential_Data *pd EINA_UNUSED)
{
   Eina_List *anim_objs = efl_animation_object_group_objects_get(eo_obj);
   if (!anim_objs) return 0.0;

   double total_duration = 0.0;
   Eina_List *l;
   Efl_Animation *anim_obj;
   EINA_LIST_FOREACH(anim_objs, l, anim_obj)
     {
        double child_total_duration =
           efl_animation_object_total_duration_get(anim_obj);

        double start_delay = efl_animation_object_start_delay_get(anim_obj);
        if (start_delay > 0.0)
          child_total_duration += start_delay;

        int child_repeat_count =
           efl_animation_object_repeat_count_get(anim_obj);
        if (child_repeat_count > 0)
          child_total_duration *= (child_repeat_count + 1);

        total_duration += child_total_duration;
     }
   return total_duration;
}

//Set how many times the given object has been repeated.
static void
_repeated_count_set(Efl_Animation_Object_Group_Sequential_Data *pd,
                    Efl_Animation_Object *anim_obj,
                    int repeated_count)
{

   Member_Object_Data *member_anim_obj_data = _member_anim_obj_data_find(pd, anim_obj);
   if (!member_anim_obj_data) return;

   member_anim_obj_data->repeated_count = repeated_count;
}

//Get how many times the given object has been repeated.
static int
_repeated_count_get(Efl_Animation_Object_Group_Sequential_Data *pd,
                    Efl_Animation_Object *anim_obj)
{
   Member_Object_Data *member_anim_obj_data =
      _member_anim_obj_data_find(pd, anim_obj);
   if (!member_anim_obj_data) return 0;

   return member_anim_obj_data->repeated_count;
}

EOLIAN static void
_efl_animation_object_group_sequential_efl_animation_object_progress_set(Eo *eo_obj,
                                                                         Efl_Animation_Object_Group_Sequential_Data *pd EINA_UNUSED,
                                                                         double progress)
{
   if ((progress < 0.0) || (progress > 1.0)) return;

   Eina_List *anim_objs = efl_animation_object_group_objects_get(eo_obj);
   if (!anim_objs) return;

   double group_total_duration =
      efl_animation_object_total_duration_get(eo_obj);

   double elapsed_time = progress * group_total_duration;

   double sum_prev_total_duration = 0.0;

   Eina_List *l;
   Efl_Animation_Object *anim_obj;
   EINA_LIST_FOREACH(anim_objs, l, anim_obj)
     {
        //Current animation object does not start
        if (sum_prev_total_duration > elapsed_time) break;

        //Sum the current total duration
        double total_duration =
           efl_animation_object_total_duration_get(anim_obj);
        double start_delay = efl_animation_object_start_delay_get(anim_obj);
        double anim_obj_progress;

        Eina_Bool start_repeat = EINA_FALSE;

        if (total_duration == 0.0)
          anim_obj_progress = 1.0;
        else
          {
             //If object is repeated, then recalculate progress.
             int repeated_count = _repeated_count_get(pd, anim_obj);
             if (repeated_count > 0)
               sum_prev_total_duration +=
                  ((total_duration + start_delay) * repeated_count);

             double elapsed_time_without_delay =
                elapsed_time - sum_prev_total_duration - start_delay;

             //Object should not start to wait for start delay time.
             if (elapsed_time_without_delay < 0.0) break;

             anim_obj_progress = elapsed_time_without_delay / total_duration;

             if (anim_obj_progress > 1.0)
               anim_obj_progress = 1.0;

             //Animation has been finished.
             if (anim_obj_progress == 1.0)
               {
                  /* If object is finished and it should be repeated, then
                   * increate the repeated count to recalculate progress. */
                  int repeat_count =
                     efl_animation_object_repeat_count_get(anim_obj);
                  if (repeat_count > 0)
                    {
                       repeated_count = _repeated_count_get(pd, anim_obj);
                       if (repeated_count < repeat_count)
                         {
                            repeated_count++;
                            _repeated_count_set(pd, anim_obj, repeated_count);

                            start_repeat = EINA_TRUE;
                         }
                    }
               }
          }

        /* Update the sum of the previous objects' total durations and start
         * delays */
        sum_prev_total_duration += (total_duration + start_delay);

        if ((anim_obj_progress == 1.0) && (!start_repeat) &&
            (!efl_animation_object_final_state_keep_get(anim_obj)))
          continue;

        /* If object is repeated with reverse mode, then the progress value
         * should be modified as (1.0 - progress). */
        Efl_Animation_Object_Repeat_Mode repeat_mode
           = efl_animation_object_repeat_mode_get(anim_obj);
        if (repeat_mode == EFL_ANIMATION_OBJECT_REPEAT_MODE_REVERSE)
          {
             int repeated_count = _repeated_count_get(pd, anim_obj);
             if (repeated_count > 0)
               {
                  if ((((repeated_count % 2) == 1) && (!start_repeat)) ||
                      (((repeated_count % 2) == 0) && (start_repeat)))
                    anim_obj_progress = 1.0 - anim_obj_progress;
               }
          }

        //Apply interpolator
        Efl_Interpolator *group_interp =
           efl_animation_object_interpolator_get(eo_obj);

        /* If group interpolator exists, then the group interpolator has been
         * already applied. So it is not needed to apply interpolator again. */
        if (!group_interp)
          {
             Efl_Interpolator *interpolator =
                efl_animation_object_interpolator_get(anim_obj);
             if (interpolator)
               {
                  anim_obj_progress =
                     efl_interpolator_interpolate(interpolator,
                                                  anim_obj_progress);
               }
          }

        efl_animation_object_progress_set(anim_obj, anim_obj_progress);
     }

   efl_animation_object_progress_set(efl_super(eo_obj, MY_CLASS), progress);
}

/* Internal EO APIs */

#define EFL_ANIMATION_OBJECT_GROUP_SEQUENTIAL_EXTRA_OPS \
   EFL_OBJECT_OP_FUNC(efl_animation_object_group_object_add, _efl_animation_object_group_sequential_efl_animation_object_group_object_add), \
   EFL_OBJECT_OP_FUNC(efl_animation_object_group_object_del, _efl_animation_object_group_sequential_efl_animation_object_group_object_del), \
   EFL_OBJECT_OP_FUNC(efl_animation_object_total_duration_get, _efl_animation_object_group_sequential_efl_animation_object_total_duration_get)

#include "efl_animation_object_group_sequential.eo.c"