Skip to main content

rust_animation/
layer.rs

1// Copyright (c) 2021 Joone Hur <joone@chromium.org> All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5extern crate gl;
6extern crate image;
7extern crate keyframe;
8
9use self::gl::types::*;
10use cgmath::{Deg, Matrix, Matrix4, SquareMatrix, Vector3};
11use image::DynamicImage;
12use std::ffi::CStr;
13use std::mem;
14use std::os::raw::c_void;
15use std::path::Path;
16use std::ptr;
17
18use stretch::{
19  node::{Node, Stretch},
20  style::*,
21};
22
23use crate::animation::Animation;
24use crate::font::FontRenderer;
25
26#[repr(i32)]
27#[derive(Copy, Clone, Debug, PartialEq)]
28pub enum Key {
29  Space = 32,
30  Enter = 36,
31  Tab = 48,
32  Backspace = 51,
33  Escape = 53,
34  Right = 262,
35  Left = 263,
36  Down = 264,
37  Up = 265,
38}
39
40#[derive(Copy, Clone, Debug)]
41pub enum LayoutMode {
42  UserDefine,
43  Flex,
44}
45
46macro_rules! c_str {
47  ($literal:expr) => {
48    CStr::from_bytes_with_nul_unchecked(concat!($literal, "\0").as_bytes())
49  };
50}
51
52pub struct RALayer {
53  pub name: String,
54  pub x: i32,
55  pub y: i32,
56  pub z: f32,
57  pub width: u32,
58  pub height: u32,
59  pub anchor_x: f32,
60  pub anchor_y: f32,
61  pub scale_x: f32,
62  pub scale_y: f32,
63  pub rotation: i32,
64  pub visible: bool,
65  color: [f32; 3],
66  pub opacity: f32, // CoreAnimation-style property
67  pub image_path: String,
68  pub sub_layer_list: Vec<RALayer>,
69  vertex_array_obj: gl::types::GLuint,
70  texture: gl::types::GLuint,
71  pub animated: bool,
72  pub animation: Option<Animation>,
73  animations: std::collections::HashMap<String, Animation>, // CoreAnimation-style animations by key
74  event_handler: Option<Box<dyn EventHandler>>,
75  layout: Option<Box<dyn Layout>>,
76  focused_sub_layer: usize,
77  focused: bool,
78  pub needs_update: bool,
79  pub node: Option<Node>,   // for stretch only
80  pub style: Option<Style>, // for stretch only
81}
82
83pub trait EventHandler {
84  fn key_focus_in(&mut self, layer: &mut RALayer);
85  fn key_focus_out(&mut self, layer: &mut RALayer);
86  fn key_down(&mut self, key: Key, layer: &mut RALayer);
87}
88
89pub trait Layout {
90  fn layout_sub_layers(
91    &mut self,
92    layer: &mut RALayer,
93    parent_layer: Option<&RALayer>,
94    stretch: &mut Option<Stretch>,
95  );
96  fn update_layout(&mut self, layer: &mut RALayer, stretch: &mut Option<Stretch>);
97  fn finalize(&mut self);
98}
99
100impl RALayer {
101  pub fn new(name: String, w: u32, h: u32, event_handler: Option<Box<dyn EventHandler>>) -> Self {
102    let mut layer = RALayer {
103      name: name,
104      x: 0,
105      y: 0,
106      z: 0.0,
107      width: w,
108      height: h,
109      anchor_x: 0.5,
110      anchor_y: 0.5,
111      scale_x: 1.0,
112      scale_y: 1.0,
113      rotation: 0,
114      visible: true,
115      color: [1.0, 1.0, 1.0],
116      opacity: 1.0,
117      image_path: "".to_string(),
118      sub_layer_list: Vec::new(),
119      vertex_array_obj: gl::types::GLuint::default(),
120      texture: gl::types::GLuint::default(),
121      animated: false,
122      animation: None,
123      animations: std::collections::HashMap::new(),
124      event_handler: event_handler,
125      layout: None,
126      focused_sub_layer: 0,
127      focused: false,
128      needs_update: true,
129      node: None,
130      style: None,
131    };
132    layer.init_gl();
133
134    layer
135  }
136
137  pub fn init_gl(&mut self) {
138    // Skip OpenGL initialization during tests
139    #[cfg(test)]
140    {
141      return;
142    }
143    
144    #[cfg(not(test))]
145    unsafe {
146      let (mut vertex_array_buffer, mut elem_array_buffer) = (0, 0);
147      let vertices: [f32; 20] = [
148        // positions                   texture coords
149        self.width as f32,
150        self.height as f32,
151        0.0,
152        1.0,
153        1.0, // top right
154        self.width as f32,
155        0.0,
156        0.0,
157        1.0,
158        0.0, // bottom right
159        0.0,
160        0.0,
161        0.0,
162        0.0,
163        0.0, // bottom left
164        0.0,
165        self.height as f32,
166        0.0,
167        0.0,
168        1.0, // top left
169      ];
170      let indices = [
171        0, 1, 3, // first Triangle
172        1, 2, 3, // second Triangle
173      ];
174
175      gl::GenVertexArrays(1, &mut self.vertex_array_obj);
176      gl::BindVertexArray(self.vertex_array_obj);
177
178      // position data
179      gl::GenBuffers(1, &mut vertex_array_buffer);
180      gl::BindBuffer(gl::ARRAY_BUFFER, vertex_array_buffer);
181      gl::BufferData(
182        gl::ARRAY_BUFFER,
183        (vertices.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
184        &vertices[0] as *const f32 as *const c_void,
185        gl::STATIC_DRAW,
186      );
187      // index data
188      gl::GenBuffers(1, &mut elem_array_buffer);
189      gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, elem_array_buffer);
190      gl::BufferData(
191        gl::ELEMENT_ARRAY_BUFFER,
192        (indices.len() * mem::size_of::<GLfloat>()) as GLsizeiptr,
193        &indices[0] as *const i32 as *const c_void,
194        gl::STATIC_DRAW,
195      );
196
197      let stride = 5 * mem::size_of::<GLfloat>() as GLsizei;
198      // position attribute
199      gl::VertexAttribPointer(0, 3, gl::FLOAT, gl::FALSE, stride, ptr::null());
200      gl::EnableVertexAttribArray(0);
201    }
202  }
203
204  pub fn set_color(&mut self, r: f32, g: f32, b: f32) {
205    self.color[0] = r;
206    self.color[1] = g;
207    self.color[2] = b;
208  }
209
210  pub fn set_text(&mut self, text: &str) {
211    let mut font_renderer: FontRenderer = FontRenderer::new("fonts/DejaVuSans.ttf".to_string());
212    let image = font_renderer.render(text);
213    let dynamic_image = DynamicImage::ImageRgba8(image);
214
215    dynamic_image.save("temp.png").unwrap();
216
217    self.image_path = "temp".to_string();
218    let stride = 5 * mem::size_of::<GLfloat>() as GLsizei;
219
220    unsafe {
221      // texture coord attribute
222      gl::VertexAttribPointer(
223        1,
224        2,
225        gl::FLOAT,
226        gl::FALSE,
227        stride,
228        (3 * mem::size_of::<GLfloat>()) as *const c_void,
229      );
230      gl::EnableVertexAttribArray(1);
231
232      // Create a texture
233      gl::GenTextures(1, &mut self.texture);
234      gl::BindTexture(gl::TEXTURE_2D, self.texture);
235      // set the texture wrapping parameters
236      gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
237      gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
238      // set texture filtering parameters
239      gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
240      gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
241
242      self.width = dynamic_image.width();
243      self.height = dynamic_image.height();
244
245      println!("width: {}, height: {}", self.width, self.height);
246
247      //   let data = image.into_raw();
248      //let data = image.into_vec();
249      let to_rgba = dynamic_image.to_rgba8();
250      let data = to_rgba.into_raw();
251      gl::TexImage2D(
252        gl::TEXTURE_2D,
253        0,
254        gl::RGBA as i32,
255        self.width as i32,
256        self.height as i32,
257        0,
258        gl::RGBA,
259        gl::UNSIGNED_BYTE,
260        data.as_ptr() as *const c_void,
261      );
262      gl::GenerateMipmap(gl::TEXTURE_2D);
263      // Unbind the texture
264      gl::BindTexture(gl::TEXTURE_2D, 0);
265    }
266  }
267
268  pub fn set_image(&mut self, path: String) {
269    self.image_path = path;
270
271    if !self.image_path.is_empty() {
272      let stride = 5 * mem::size_of::<GLfloat>() as GLsizei;
273      unsafe {
274        // texture coord attribute
275        gl::VertexAttribPointer(
276          1,
277          2,
278          gl::FLOAT,
279          gl::FALSE,
280          stride,
281          (3 * mem::size_of::<GLfloat>()) as *const c_void,
282        );
283        gl::EnableVertexAttribArray(1);
284
285        // Create a texture
286        gl::GenTextures(1, &mut self.texture);
287        gl::BindTexture(gl::TEXTURE_2D, self.texture);
288        // set the texture wrapping parameters
289        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
290        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
291        // set texture filtering parameters
292        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
293        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
294
295        match image::open(&Path::new(&self.image_path)) {
296          Ok(img) => {
297            let to_rgba = img.to_rgba8();
298            let data = to_rgba.into_vec();
299            gl::TexImage2D(
300              gl::TEXTURE_2D,
301              0,
302              gl::RGB as i32,
303              img.width() as i32,
304              img.height() as i32,
305              0,
306              gl::RGBA,
307              gl::UNSIGNED_BYTE,
308              &data[0] as *const u8 as *const c_void,
309            );
310            gl::GenerateMipmap(gl::TEXTURE_2D);
311          }
312          Err(err) => println!("Fail to load a image {:?}", err),
313        }
314      }
315    }
316  }
317
318  pub fn set_layout(&mut self, layout: Option<Box<dyn Layout>>) {
319    self.layout = layout;
320  }
321
322  pub fn set_animation(&mut self, animation: Option<Animation>) {
323    self.animation = animation;
324  }
325
326  pub fn set_style(&mut self, style: Style) {
327    self.style = Some(style);
328  }
329
330  pub fn set_visible(&mut self, visible: bool) {
331    self.visible = visible;
332  }
333
334  /*pub fn update(&mut self) {
335    // Sort sub actors by z-axis
336    self.sub_layer_list.sort_by(|a, b| a.z.partial_cmp(&b.z).unwrap());
337  }*/
338
339  pub fn animate(&mut self) {
340    // Run legacy animation if present
341    if let Some(mut animation) = self.animation.take() {
342      animation.run(self);
343      self.animation = Some(animation);
344    }
345
346    // Run CoreAnimation-style animations
347    // Take the animations HashMap out temporarily
348    let mut animations = std::mem::take(&mut self.animations);
349    for (_key, animation) in animations.iter_mut() {
350      animation.run(self);
351    }
352    // Put it back
353    self.animations = animations;
354
355    for sub_layer in self.sub_layer_list.iter_mut() {
356      sub_layer.animate();
357    }
358  }
359
360  pub fn select_next_sub_layer(&mut self) {
361    if self.sub_layer_list.is_empty() {
362      return;
363    }
364    // no more next layer.
365    if self.focused_sub_layer < self.sub_layer_list.len() - 1 {
366      let prev_focused_sub_layer = self.focused_sub_layer;
367      self.focused_sub_layer += 1;
368      self.sub_layer_list[self.focused_sub_layer].set_focus(true);
369      self.sub_layer_list[prev_focused_sub_layer].set_focus(false);
370    }
371  }
372
373  pub fn select_prev_sub_layer(&mut self) {
374    if self.sub_layer_list.is_empty() {
375      return;
376    }
377    // ne more previous layer.
378    if self.focused_sub_layer == 0 {
379      return;
380    }
381    let prev_focused_sub_layer = self.focused_sub_layer;
382    self.focused_sub_layer -= 1;
383    self.sub_layer_list[self.focused_sub_layer].set_focus(true);
384    self.sub_layer_list[prev_focused_sub_layer].set_focus(false);
385  }
386
387  pub fn set_focus(&mut self, focused: bool) {
388    self.focused = focused;
389    if let Some(mut event_handler) = self.event_handler.take() {
390      //println!("set_focus {} {} ", self.name, focused);
391
392      if self.focused {
393        event_handler.key_focus_in(self);
394      } else {
395        event_handler.key_focus_out(self);
396      }
397      self.event_handler = Some(event_handler);
398    }
399  }
400
401  pub fn handle_input(&mut self, key: Key) {
402    for sub_layer in self.sub_layer_list.iter_mut() {
403      if sub_layer.focused {
404        sub_layer.handle_input(key);
405      }
406    }
407    if let Some(mut event_handler) = self.event_handler.take() {
408      event_handler.key_down(key, self);
409      self.event_handler = Some(event_handler);
410    }
411  }
412
413  pub fn layout_sub_layers(&mut self, parent_layer: Option<&RALayer>, stretch: &mut Option<Stretch>) {
414    if let Some(mut layout) = self.layout.take() {
415      layout.layout_sub_layers(self, parent_layer, stretch);
416      self.layout = Some(layout); // Put back the layout
417    }
418
419    // Replace the sub_layer_list with an empty vector and take the original vector out
420    let mut sub_layer_list = std::mem::replace(&mut self.sub_layer_list, Vec::new());
421
422    // Iterate over the vector outside of the self structure
423    for sub_layer in &mut sub_layer_list {
424      // As we are outside of the self structure, we can now borrow self as immutable
425      sub_layer.layout_sub_layers(Some(self), stretch);
426    }
427
428    // Put back the original sub_layer_list
429    self.sub_layer_list = sub_layer_list;
430  }
431
432  pub fn update_layout(&mut self, stretch: &mut Option<Stretch>) {
433    if let Some(mut layout) = self.layout.take() {
434      layout.update_layout(self, stretch);
435      self.layout = Some(layout); // Put back the layout
436    }
437
438    for sub_layer in self.sub_layer_list.iter_mut() {
439      sub_layer.update_layout(stretch);
440    }
441  }
442
443  pub fn finalize_layout(&mut self) {
444    if let Some(ref mut layout) = self.layout {
445      layout.finalize();
446    }
447  }
448
449  pub fn model_matrix(&self) -> Matrix4<f32> {
450    let mut transform: Matrix4<f32> = Matrix4::identity();
451    transform = transform
452      * Matrix4::<f32>::from_translation(Vector3::new(self.x as f32, self.y as f32, self.z as f32));
453
454    // Handle rotation and scale.
455    // Move back to the original position.
456    transform = transform
457      * Matrix4::<f32>::from_translation(Vector3::new(
458        self.width as f32 * self.anchor_x,
459        self.height as f32 * self.anchor_y,
460        0.0,
461      ));
462
463    if self.rotation != 0 {
464      transform = transform * Matrix4::<f32>::from_angle_z(Deg(self.rotation as f32));
465    }
466
467    transform = transform * Matrix4::from_nonuniform_scale(self.scale_x, self.scale_y, 0.0);
468
469    // Move to the origin of coordinate.
470    transform = transform
471      * Matrix4::<f32>::from_translation(Vector3::new(
472        -(self.width as f32 * self.anchor_x),
473        -(self.height as f32 * self.anchor_y),
474        0.0,
475      ));
476
477    transform
478  }
479
480  pub fn render(
481    &mut self,
482    shader_program: GLuint,
483    parent_model_matrix: Option<&Matrix4<f32>>,
484    projection: &Matrix4<f32>,
485  ) {
486    if !self.visible {
487      return;
488    }
489
490    let mut transform: Matrix4<f32> = self.model_matrix();
491    if let Some(parent_model_matrix) = parent_model_matrix {
492      transform = transform * parent_model_matrix;
493    }
494
495    unsafe {
496      gl::UseProgram(shader_program);
497      let loc_color = gl::GetUniformLocation(shader_program, c_str!("color").as_ptr());
498      let loc_transform = gl::GetUniformLocation(shader_program, c_str!("transform").as_ptr());
499      let loc_projection = gl::GetUniformLocation(shader_program, c_str!("projection").as_ptr());
500      let loc_use_texture = gl::GetUniformLocation(shader_program, c_str!("useTexture").as_ptr());
501
502      gl::Uniform4f(loc_color, self.color[0], self.color[1], self.color[2], self.opacity);
503      gl::UniformMatrix4fv(loc_transform, 1, gl::FALSE, transform.as_ptr());
504      gl::UniformMatrix4fv(loc_projection, 1, gl::FALSE, projection.as_ptr());
505
506      if !self.image_path.is_empty() {
507        gl::BindTexture(gl::TEXTURE_2D, self.texture);
508        gl::Uniform1i(loc_use_texture, 1);
509      } else {
510        gl::Uniform1i(loc_use_texture, 0);
511      }
512
513      gl::BindVertexArray(self.vertex_array_obj);
514      gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
515    }
516
517    for sub_layer in self.sub_layer_list.iter_mut() {
518      if sub_layer.focused == false {
519        sub_layer.render(shader_program, Some(&transform), projection);
520      }
521    }
522
523    // render the focused sub_layer at the end.
524    if !self.sub_layer_list.is_empty() {
525      self.sub_layer_list[self.focused_sub_layer].render(
526        shader_program,
527        Some(&transform),
528        projection,
529      );
530    }
531  }
532
533  pub fn add_sub_layer(&mut self, layer: RALayer) {
534    self.sub_layer_list.push(layer);
535  }
536
537  // CoreAnimation-style API methods
538
539  /// Set position (CoreAnimation-style API)
540  pub fn set_position(&mut self, x: i32, y: i32) {
541    self.x = x;
542    self.y = y;
543  }
544
545  /// Get position as tuple (CoreAnimation-style API)
546  pub fn position(&self) -> (i32, i32) {
547    (self.x, self.y)
548  }
549
550  /// Set bounds (CoreAnimation-style API)
551  pub fn set_bounds(&mut self, width: u32, height: u32) {
552    self.width = width;
553    self.height = height;
554  }
555
556  /// Get bounds as tuple (CoreAnimation-style API)
557  pub fn bounds(&self) -> (u32, u32) {
558    (self.width, self.height)
559  }
560
561  /// Set opacity (CoreAnimation-style API)
562  pub fn set_opacity(&mut self, opacity: f32) {
563    self.opacity = opacity.max(0.0).min(1.0);
564  }
565
566  /// Set background color (CoreAnimation-style API)
567  pub fn set_background_color(&mut self, r: f32, g: f32, b: f32) {
568    self.set_color(r, g, b);
569  }
570
571  /// Get background color (CoreAnimation-style API)
572  pub fn background_color(&self) -> (f32, f32, f32) {
573    (self.color[0], self.color[1], self.color[2])
574  }
575
576  /// Add an animation for a specific key (CoreAnimation-style API)
577  pub fn add_animation(&mut self, animation: Animation, key: Option<&str>) {
578    if let Some(key_str) = key {
579      self.animations.insert(key_str.to_string(), animation);
580    } else {
581      // If no key provided, use the legacy animation field
582      self.animation = Some(animation);
583    }
584  }
585
586  /// Remove all animations (CoreAnimation-style API)
587  pub fn remove_all_animations(&mut self) {
588    self.animations.clear();
589    self.animation = None;
590  }
591
592  /// Remove animation for a specific key (CoreAnimation-style API)
593  pub fn remove_animation(&mut self, key: &str) {
594    self.animations.remove(key);
595  }
596
597  /// Add a sublayer (CoreAnimation-style API, alias for add_sub_layer)
598  pub fn add_sublayer(&mut self, layer: RALayer) {
599    self.add_sub_layer(layer);
600  }
601
602  /// Get sublayers (CoreAnimation-style API)
603  pub fn sublayers(&self) -> &Vec<RALayer> {
604    &self.sub_layer_list
605  }
606
607  /// Get mutable sublayers (CoreAnimation-style API)
608  pub fn sublayers_mut(&mut self) -> &mut Vec<RALayer> {
609    &mut self.sub_layer_list
610  }
611}
612
613#[cfg(test)]
614mod tests {
615  use super::*;
616  use crate::animation::{Animation, EasingFunction};
617
618  #[test]
619  fn test_position_api() {
620    let mut layer = RALayer::new("test".to_string(), 100, 100, None);
621    layer.set_position(50, 75);
622    let (x, y) = layer.position();
623    assert_eq!(x, 50);
624    assert_eq!(y, 75);
625  }
626
627  #[test]
628  fn test_bounds_api() {
629    let mut layer = RALayer::new("test".to_string(), 100, 100, None);
630    layer.set_bounds(200, 150);
631    let (w, h) = layer.bounds();
632    assert_eq!(w, 200);
633    assert_eq!(h, 150);
634  }
635
636  #[test]
637  fn test_opacity_api() {
638    let mut layer = RALayer::new("test".to_string(), 100, 100, None);
639    assert_eq!(layer.opacity, 1.0);
640    
641    layer.set_opacity(0.5);
642    assert_eq!(layer.opacity, 0.5);
643    
644    // Test clamping
645    layer.set_opacity(1.5);
646    assert_eq!(layer.opacity, 1.0);
647    
648    layer.set_opacity(-0.5);
649    assert_eq!(layer.opacity, 0.0);
650  }
651
652  #[test]
653  fn test_background_color_api() {
654    let mut layer = RALayer::new("test".to_string(), 100, 100, None);
655    layer.set_background_color(0.5, 0.6, 0.7);
656    let (r, g, b) = layer.background_color();
657    assert_eq!(r, 0.5);
658    assert_eq!(g, 0.6);
659    assert_eq!(b, 0.7);
660  }
661
662  #[test]
663  fn test_add_animation_with_key() {
664    let mut layer = RALayer::new("test".to_string(), 100, 100, None);
665    let mut animation = Animation::with_key_path("position.x");
666    animation.duration = 2.0;
667    animation.timing_function = Some(EasingFunction::Linear);
668    
669    layer.add_animation(animation, Some("moveX"));
670    assert_eq!(layer.animations.len(), 1);
671    assert!(layer.animations.contains_key("moveX"));
672  }
673
674  #[test]
675  fn test_remove_animation() {
676    let mut layer = RALayer::new("test".to_string(), 100, 100, None);
677    let animation1 = Animation::with_key_path("position.x");
678    let animation2 = Animation::with_key_path("opacity");
679    
680    layer.add_animation(animation1, Some("anim1"));
681    layer.add_animation(animation2, Some("anim2"));
682    assert_eq!(layer.animations.len(), 2);
683    
684    layer.remove_animation("anim1");
685    assert_eq!(layer.animations.len(), 1);
686    assert!(!layer.animations.contains_key("anim1"));
687    assert!(layer.animations.contains_key("anim2"));
688  }
689
690  #[test]
691  fn test_remove_all_animations() {
692    let mut layer = RALayer::new("test".to_string(), 100, 100, None);
693    let animation1 = Animation::with_key_path("position.x");
694    let animation2 = Animation::with_key_path("opacity");
695    let animation3 = Animation::new();
696    
697    layer.add_animation(animation1, Some("anim1"));
698    layer.add_animation(animation2, Some("anim2"));
699    layer.set_animation(Some(animation3));
700    
701    assert_eq!(layer.animations.len(), 2);
702    assert!(layer.animation.is_some());
703    
704    layer.remove_all_animations();
705    assert_eq!(layer.animations.len(), 0);
706    assert!(layer.animation.is_none());
707  }
708
709  #[test]
710  fn test_sublayers_api() {
711    let mut parent = RALayer::new("parent".to_string(), 200, 200, None);
712    let child1 = RALayer::new("child1".to_string(), 50, 50, None);
713    let child2 = RALayer::new("child2".to_string(), 50, 50, None);
714    
715    parent.add_sublayer(child1);
716    parent.add_sublayer(child2);
717    
718    let sublayers = parent.sublayers();
719    assert_eq!(sublayers.len(), 2);
720    assert_eq!(sublayers[0].name, "child1");
721    assert_eq!(sublayers[1].name, "child2");
722  }
723
724  #[test]
725  fn test_backward_compatibility() {
726    let mut layer = RALayer::new("test".to_string(), 100, 100, None);
727    
728    // Old way of setting position
729    layer.x = 50;
730    layer.y = 75;
731    assert_eq!(layer.x, 50);
732    assert_eq!(layer.y, 75);
733    
734    // Old way of creating animation
735    let mut animation = Animation::new();
736    animation.apply_translation_x(0, 100, 1.0, EasingFunction::Linear);
737    layer.set_animation(Some(animation));
738    
739    assert!(layer.animation.is_some());
740  }
741}