edc를 이용하여 간단한 3D 효과를 줄 수 있습니다.

정밀하게 조절하기가 힘들어서 혹은 조절할 수가 없어서

정교한 효과와는 거리가 있습니다만,

지루한 2D 효과에 지쳤다면 한 번쯤 도전해볼 필요가 있습니다.


이번 포스팅에서는 원근효과를 주는 방법에 대해 살펴보겠습니다.



안녕하세요, Tizen 개발자 윤진입니다.


3D 효과를 적용하기 위해서는 map 블록을 사용해야 합니다.

collections - group - parts - part - description - map

위의 구조의 종단에 map 블록이 있습니다.

description 에에 있다는 것은 program으로 map의 상태를 변경하여 3D 효과를 줄 수 있다는 겁니다.


코드 상에서 map을 보면 아래와 같은 필드들과 사용됩니다.

description {
        ..
        map {
            perspective: "다른 파트이름";
            light: "이름";
            on: 1;
            smooth: 1;
            perspective_on: 1;
            backface_cull: 1;
            alpha: 1;
            rotation {
                ...
            }
        }
        ...
    }


위의 map 블록에서 사용한 필드-

perspective, light, on, smooth, perspective_on, backface_cull, alpha, rotate

우선 위의 필드 중 확대/축소와 관련된 필드인 perspective를 살펴보도록 하겠습니다.


- perspective: [다른 파트이름];

perspective 필드에는 현재 파트의 zplane을 정할때 참조하는 파트를 적어줍니다.

참조하는 파트의 zplane에 따라 현재 파트의 zplane도 확정됩니다.


그렇다면, zplane은 무엇일까요?

zplane은 2차원의 x축과 y축에 이은 3차원의 z축을 의미합니다.

zplane의 설정값에 따라 파트의 멀고 가까움이 결정됩니다.

zplane의 값이 커지면 파트가 가까워져서 커지고,

zplane의 값이 작아지면 파트가 점점 멀어져서 작아집니다.


위의 참조하는 파트는 아래와 같이 perspective 블록을 사용하여 zplane을 설정해야합니다.

       part {
         name: "zoom_out";
         type: RECT;
         description {
           state: "default" 0.0;
           perspective {
             zplane: -140;
           }
           visible: 0;
         }
       } 


perspective 필드에 사용할 파트는 화면에 보이지 않아도 됩니다.

따라서 color 필드를 사용하지 않았고,

visible을 '0'으로 설정하였습니다.

하지만, 영역만큼은 반드시 가지고 있어야 하기에 RECT 타입을 사용하였습니다.

(SPACER 타입도 사용하면 안됩니다)

rel1 / rel2를 사용하진 않았지만,

default로 rel1은 0.0으로 rel2는 1.0으로 설정되니 영역이 잡힌거나 다름없습니다.

perspective는 이 영역의 정가운데 점 하나만을 perspective 로 활용합니다.

zplane은 -140이니 이 파트에 perspective를 걸어둔다면 파트가 작아질 것입니다.


       part {
         name: "zoom_in";
         type: RECT;
         description {
           state: "default" 0.0;
           perspective {
             zplane: 140;
           }
           visible: 0;
         }
       }

위의 파트는 zplane이 140입니다.

perspective로 위의 파트를 걸어두면 파트는 더 커지게 됩니다.



- on: [0 or 1];

map 블록을 활성화(1) 혹은 비활성화(0) 시킵니다.

default가 비활성화이기 때문에 map 블록을 사용하려면 반드시 on: 1;을 해주어야 합니다.



perspective를 확인하기 위해서 우선 rectangle을 3개 만들어보겠습니다.

       part {
         name: "rect_1";
         type: RECT;
         description {
             state: "default" 0.0;
             rel1 { relative: 0.1 0.1; to: "bg"; }
             rel2 { relative: 0.2 0.2; to: "bg"; }
             color: 100 0 100 255;
             visible: 1;
           }
         }
         part {
           name: "rect_2";
           type: RECT;
           description {
             state: "default" 0.0;
             rel1 { relative: 0.2 0.2; to: "bg"; }
             rel2 { relative: 0.3 0.3;  to: "bg"; }
             color: 150 0 150 255;
             visible: 1;
           }
         }
         part {
           name: "rect_3";
           type: RECT;
           description {
             state: "default" 0.0;
             rel1 { relative: 0.3 0.3; to: "bg"; }
             rel2 { relative: 0.4 0.4; to: "bg"; }
             color: 200 0 200 255;
             visible: 1;
           }
         }


색상을 점차 밝은 색으로 변경하며 세 개의 사각형을 그렸습니다.

세 개의 사각형 모두 relative 0.1의 폭과 높이를 가지고 있습니다.

실제로는 아래와 같이 보이겠지요.



이번에는 사각형에 각각의 perspective를 적용합니다.

제일 상단의 사각형에는 -140의 zplane이 적용된 파트를 보게 하고,

제일 하단의 사각형은 140의 zplane이 적용된 파트를 보게합니다.

중간에 있는 사각형은 zplane이 없습니다.


그리고 zplane이 적용된 파트는 전체화면을 영역으로 가지도록 만들겠습니다.

전체영역을 가지고 있다고 하더라도,

실제 perspective가 되는 것은 영역의 정가운데에 점입니다.

정가운데 점이 perspective 관점이 되어서 다른 파트의 원근을 조정합니다.

영역으로는 원근법의 중심점을 정하고,

zplane으로는 원근을 정합니다.


collections {
   group {
     name: "main";
     parts {
       part {
         name: "bg";
         type: RECT;
         description {
           state: "default" 0.0;
           color: 255 255 255 255;
           visible: 1;
         }
       }
       part {
         name: "zoom_out";
         type: RECT;
         description {
           state: "default" 0.0;
           perspective {
             zplane: -140;
           }
           visible: 0;
         }
       } 
       part {
         name: "zoom_in";
         type: RECT;
         description {
           state: "default" 0.0;
           perspective {
             zplane: 140;
           }
           visible: 0;
         }
       }
       part {
         name: "rect_1";
         type: RECT;
         description {
             state: "default" 0.0;
             rel1 { relative: 0.1 0.1; to: "bg"; }
             rel2 { relative: 0.2 0.2; to: "bg"; }
             color: 100 0 100 255;
             visible: 1;
             map {
               perspective: "zoom_out";
               on: 1;
             }
           }
         }
         part {
           name: "rect_2";
           type: RECT;
           description {
             state: "default" 0.0;
             rel1 { relative: 0.2 0.2; to: "bg"; }
             rel2 { relative: 0.3 0.3;  to: "bg"; }
             color: 150 0 150 255;
             visible: 1;
           }
         }
         part {
           name: "rect_3";
           type: RECT;
           description {
             state: "default" 0.0;
             rel1 { relative: 0.3 0.3; to: "bg"; }
             rel2 { relative: 0.4 0.4; to: "bg"; }
             color: 200 0 200 255;
             visible: 1;
             map {
               perspective: "zoom_in";
               on: 1;
             }
           }
         }
     }
  }
}


rect1 파트는 가운데 점에서 봤을때,

zplane이 -140이 되어 더 작아집니다.

더 작아지지만 원래 있던 위치보다 가운데 쪽으로 '많이' 다가오게 됩니다.

이는 소실점이 있는 원근법을 생각하시면 될 겁니다.

1m가 떨어진 두 물체를 가까이에서 보면 1m의 거리만큼으로 보이겠지만,

먼 거리에서 보면 1cm로 보일 수도 있겠죠.

물체가 멀어지면 멀어질수록 가운데로 다가오게 됩니다.


rect3 파트는 가운데 점에서 봤을때,

zplane이 140이 되어서 더 커집니다.

더 커지지만 원래 있던 위치보다 가운데 쪽에서 '조금' 멀어지게 됩니다.

완전히 가운데 있지 않는 한,

눈에 가까이 올수록 가운데에서 멀어지게 되는 것이지요.


종합하자면 아래 그림처럼,

rect1은 작아지고 rect2쪽으로 다가옵니다.

rect3는 커지면서 rect2로 멀어집니다.



오늘은 perspective에 대해서 알아보았습니다.

그럼 좋은 하루 보내세요~

끝_



edc파일로 문자열을 출력할 수도 있습니다.

문자열은 edc 파일에서 직접 입력할 수도 있고,

C파일에서 elm_object_part_text_set() 함수를 사용하여,

필요할 때마다 문자열을 입력할 수 있습니다.


문자열이 전혀 없는 앱은 거의 없을 겁니다.

복잡다단한 문자표현의 세계에 첫 발을 text 블록으로 내딛어 보겠습니다.



안녕하세요, Tizen 개발자 윤진입니다.


edc에 문자열을 지원하는 타입은 2가지가 있습니다.

- TEXT

- TEXTBLOCK

위의 두 가지 중에 TEXT 타입을 먼저 살펴보겠습니다.

TEXT는 TEXTBLOCK에 비하여 간단한 문자열을 표시하는데 사용하지요.

TEXT 타입이 TEXTBLOCK 타입에 비해 multiline이라든가 기능에는 제한은 있지만,

간단한 연산만 필요하기에 훨씬 빠릅니다.



- text {}

텍스트 파트에는 텍스트에 대한 추가정보를 기술하기 위한 text 블록이 필요합니다.

text {}는 part - description 내에 위치하지요.

description에 위치한다는 것은,

각 상태(state)에 따라 text의 설정값을 변경할 수 있다는 것이겠죠.

곧, program으로 텍스트의 모양새를 여러가지로 바꿀 수 있습니다.



- text: "문자열";

text 블록 내에 블록과 동일한 이름의 필드인 text를 사용할 수 있습니다.

text 필드에 명시한 문자열은 default로 사용되는 문자열입니다.

default는 외부에서 해당 텍스트 파트에 아무 것도 set하지 않으면 나타납니다.

edc에서 문자열을 확정할 수 있다면 text 필드를 사용하면 됩니다.



- font: "폰트명";

텍스트에 폰트를 지정할 수 있습니다.

font를 지정하려면 플랫폼에서 지정한 값을 넣어줘야 하겠지요.

"Sans"나 "Arial"과 같은 글꼴 이름을 적어주면 됩니다.

물론 해당 글꼴이 플랫폼에 탑재가 되어 있어야겠지요?



- size: [폰트사이즈];

폰트 사이즈를 입력할 수 있습니다.

양수를 적어넣으면 됩니다.



- align: [x축, 0.0~1.0] [y축, 0.0~1.0];

텍스트 파트에서 정렬을 하기 위해서 align을 사용합니다.

두 개의 인자를 적어주면 되는데 각각 x축/y축 기준입니다.

텍스트 파트에서 rel1/rel2로 지정한 파트의 영역 내에서,

텍스트의 배열을 어떻게 설정할지를 align으로 정하게 됩니다.


collections {
   group {
     name: "main";
   parts {
      part {
         name: "label";
         type: TEXT;
         scale: 1;
         description {
            state: "default" 0.0;
            color: 255 0 0 255;
            text {
               text: "TEXT";
               font: "Sans";
               size: 24;
               align: 0.5 0.5;
            }
         }
      }
   }
}

위의 코드에서는 화면 한가운데에 "TEXT"라는 문자열을 표시하지요.

폰트는 "Sans"이고 폰트사이즈는 24입니다.




- effect: [EFFECT];

폰트에 이펙트를 추가할 수 있습니다.

effect 필드는 description이 아닌 part에 넣어주어야 합니다.

그 말은 state에 따라 effect를 바꿀 수 없다는 것이겠지요.

이펙트는 종류가 많아보이긴 하지만 따지고 보면 윤곽선, 그림자로 귀결됩니다


•    PLAIN : effect가 없습니다.
•    OUTLINE  : 윤곽선
•    SOFT_OUTLINE : 부드러운 윤곽선
•    SHADOW : 그림자
•    SOFT_SHADOW : 부드러운 그림자
•    OUTLINE_SHADOW : 윤곽선 + 그림자
•    OUTLINE_SOFT_SHADOW : 윤곽선 + 부드러운 그림자
•    FAR_SHADOW : 멀리 떨어진 그림자
•    FAR_SOFT_SHADOW : 멀리 떨어진 부드러운 그림자
•    GLOW : 발광윤곽선


눈으로 직접 보는게 이해가 빠르겠네요.

아래처럼 9개의 이펙트를 위한 그룹을 만들어보겠습니다.


예시에 보면 color3가 쓰였는데요,

color3는 윤곽선이나 그림자 색상을 의미합니다.

color3는 color와 함께 description에 쓰이므로 state에 따라 바꿀 수 있습니다.


collections {
   group {
     name: "main";
     parts {
       part {
         name: "bg";
         type: RECT;
         description {
           state: "default" 0.0;
           color: 255 255 255 255;
         }
       }

       part {
         name: "PLAIN";
         type: TEXT;
         effect: PLAIN;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           text {
             text: "PLAIN EFFECT";
             font: "Sans";
             size: 20;
             align: 0.5 0.0;
            }
         }
      }
     
      part {
         name: "OUTLINE";
         type: TEXT;
         effect: OUTLINE;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           color3: 0 0 0 255;
           text {
              text: "OUTLINE EFFECT";
              font: "Sans";
              size: 20;
              align: 0.5 0.1;
            }
         }
      }
       part {
         name: "SOFT_OUTLINE";
         type: TEXT;
         effect: SOFT_OUTLINE;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           color3: 0 0 0 255;
           text {
              text: "SOFT_OUTLINE EFFECT";
              font: "Sans";
              size: 20;
              align: 0.5 0.2;
            }
         }
      }
 
      part {
         name: "SHADOW";
         type: TEXT;
         effect: SHADOW;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           color3: 0 0 0 255;
            text {
               text: "SHADOW EFFECT";
               font: "Sans";
               size: 20;
               align: 0.5 0.3;
            }
         }
      }
     
      part {
         name: "SOFT_SHADOW";
         type: TEXT;
         effect: SOFT_SHADOW;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           color3: 0 0 0 255;
            text {
               text: "SOFT_SHADOW EFFECT";
               font: "Sans";
               size: 20;
               align: 0.5 0.4;
            }
         }
      }
 
      part {
         name: "OUTLINE_SHADOW";
         type: TEXT;
         effect: OUTLINE_SHADOW;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           color3: 0 0 0 255;
            text {
               text: "OUTLINE_SHADOW EFFECT";
               font: "Sans";
               size: 20;
               align: 0.5 0.5;
            }
         }
      }
 
      part {
         name: "OUTLINE_SOFT_SHADOW";
         type: TEXT;
         effect: OUTLINE_SOFT_SHADOW;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           color3: 0 0 0 255;
           text {
               text: "OUTLINE_SOFT_SHADOW EFFECT";
               font: "Sans";
               size: 20;
               align: 0.5 0.6;
            }
         }
      }
 
      part {
         name: "FAR_SHADOW";
         type: TEXT;
         effect: FAR_SHADOW;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           color3: 0 0 0 255;
            text {
               text: "FAR_SHADOW EFFECT";
               font: "Sans";
               size: 20;
               align: 0.5 0.7;
            }
         }
      }
 
      part {
         name: "FAR_SOFT_SHADOW";
         type: TEXT;
         effect: FAR_SOFT_SHADOW;
         description {
            state: "default" 0.0;
            color: 255 0 255 255;
            color3: 0 0 0 255;
            text {
               text: "FAR_SOFT_SHADOW EFFECT";
               font: "Sans";
               size: 20;
               align: 0.5 0.8;
            }
         }
      }
 
      part {
         name: "GLOW";
         type: TEXT;
         effect: GLOW;
         description {
           state: "default" 0.0;
           color: 255 0 255 255;
           color3: 0 0 0 255;
            text {
               text: "GLOW EFFECT";
               font: "Sans";
              size: 20;
               align: 0.5 0.9;
            }
         }
      }
   }
}



오늘은 여기까지 하겠습니다.

그럼 좋은 하루 보내세요~

끝_

 
 
 
 

  1. YOhoho 2015.11.23 14:58

    color2는 윤곽선
    color3는 그림자 색상이군요

    • 안녕하세요, YOhoho님. 또 오셨군요. 감사합니다 :) 컬러 뒤에 붙는 숫자가 커질수록 텍스트에서 멀어지죠~ 근데 솔직히 필드이름에 숫자가 들어간게 맞는지는 모르겠네요 :)

오늘은 파트의 상태를 적어놓는 description에서,

가장 중요한 필드 중에 하나인 rel을 살펴보도록 하겠습니다.


rel은 파트의 위치와 크기를 관장하는 필드입니다.

형태를 가지고 있는 물체에는 위치와 크기 속성이 필수인만큼,

이 필드는 꼼꼼이 잘 살펴볼 필요가 있습니다.



안녕하세요, Tizen 개발자 윤진입니다.


rel 필드는,

collections - group - parts - part - description 아래에 위치하는 필드입니다.

description 안에 있다는 것만으로도,

rel 필드는 state에 따라 변화가 가능한 필드라는 것을 알 수 있습니다.


rel은 2개의 필드가 하나의 쌍으로 동작합니다.

rel1과 rel2가 각각 시작점, 끝점의 역할을 담당하게 되지요.

이 두 점으로 사각의 영역을 얻을 수 있습니다.

part는 이 두 점으로 기준으로 위치와 크기가 결정됩니다.



- rel1/rel2 

rel1과 rel2는 각각의 블록을 가지고 있습니다.

rel1 { ... }

rel2 { ... }

위와 같이 독립된 블록을 가집니다.

이름만 봐서는 rel3, rel4가 있어도 이상해보이진 않지만,

직사각형 영역으로 형태를 표현하는 것이 충분하다고 여기는지 2개의 필드만 사용하고 있습니다.

정말 그러한지는 각자 좀 더 고민해보시죠.


rel1은 왼쪽 상단 모서리를 의미합니다.

rel2는 오른쪽 하단 모서리를 의미하지요.

이 두 점만으로 직사각형을 표현할 수 있습니다.

rel1과 rel2 블록 안에 명시한 내용이 각각 모서리 위치를 구체화 합니다.



- relative: [X축] [Y축];

rel 블록에서 가장 먼저 등장하는 것이 relative입니다.

relative는 상대적인 위치를 나타냅니다.

화면에 표시되는 영역은 0.0 ~ 1.0 범위입니다.

x축/y축 기준으로 비율에 맞게 화면에 표시가 됩니다.


collections {
  group {
    name: "storycompiler";
    parts {
      part {
        name: "red_rectangle";
        type: RECT;
        description {
          state: "default" 0.0;
          rel1.relative: 0.0 0.5;
          rel2.relative: 1.0 1.0;
          color: 255 0 0 255;
          visible: 1;
        }
      }
    }
  }
}


rel1에 0.0 0.5를 적어놓으면,

시작점은 X축 기준으로는 0이고, y축 기준으로는 가운데입니다.

rel2에 1.0 1.0을 적어놓으면,

끝점은 x축/y축 모두 마지막 좌표입니다.

위의 코드를 출력한 결과는 아래와 같습니다.



0.0 이하 혹은 1.0 이상 영역은 화면 밖에 표시가 됩니다.

화면 밖에서 화면 안으로 들어오는 애니메이션 효과를 주고 싶을때,

-1.0 이나 2.0 과 같은 수치를 써주곤 합니다.


rel1과 rel2에 같은 값을 표기할 수도 있습니다.

시작점과 끝점이 같은 값이라면 결과는 '점'입니다.

점은 화면에 표시가 되지 않습니다.


그렇다면 왜 점을 사용하는 것일까요?

점은 min값과 좋은 짝이 됩니다.

점과 min을 함께 사용한다면,

점은 위치를 나타내고,

min이 크기를 지정하게 될 겁니다.

(min값과 관련된 내용은 이 포스팅을 참고해주세요)


만약 시작점과 끝점을 달리하여 영역을 만들고 추가로 min값을 넣는 경우,

논리적 모순이 생길 수도 있습니다.

rel1/rel2는 비율로 크기를 지정하는데,

그 크기가 min값과 다를 수 있기 때문입니다.


예를 들어, 화면의 사이즈가 100 x 100이고,

rel1은 0.0 0.0, rel2는 0.1 0.1으로 지정했다고 하죠.

rel로 잡은 영역은 10 x 10의 크기입니다.

하지만, 거기에 min값으로 50 50을 주면,

어떤 값을 선택해야할지 알 수가 없게됩니다.

10 10일까요? 50 50일까요?


바로 이 점이 edc를 어렵게 만드는 점이기도 한데요,

하나의 필드에 두 개의 역할(위치, 크기)를 부여하였기 때문에,

크기를 나타내는 min과 충돌이 날 수 있습니다.

따라서 min으로 크기를 설정하고자 하면,

반드시 rel로는 점을 만드시는게 좋습니다.



- to: [다른 파트 이름];

to는 굉장히 유용하게 사용되는 필드입니다.

위에서 rel1/rel2로 위치를 잡을때 화면 전체를 기준으로 설명하였는데요,

to 필드를 추가하면, to 필드에 명시한 파트를 기준으로 rel1 / rel2가 지정됩니다.

반대로 to 필드가 없으면 전체화면을 기준으로 위치를 잡게 되겠지요.


collections {
   group {
      name: "to_example";
 
      parts {
         part {
            name: "bg";
            type: RECT;
            scale: 1;
            description {
               state: "default" 0.0;
               rel1.relative: 0.0 0.0;
               rel2.relative: 1.0 1.0;
            }
         }
         part {
            name: "region";
            type: RECT;
            scale: 1;
            description {
               state: "default" 0.0;
               color: 255 0 0 255;
               rel1.relative: 0.2 0.2;
               rel1.to:"bg";
               rel2.relative:0.8 0.8;
               rel2.to:"bg";
            }
         }
      }
   }
}



- to_x: [다른 파트 이름];

- to_y: [다른 파트 이름];

위에서 설명한 to는 to_x와 to_y가 동일한 경우에 사용합니다.

만약 x축 기준영역과 y축 기준영역이 다를 경우,

to_x와 to_y를 사용하면 됩니다.

파트의 위치가 반드시 한 파트를 기준으로 정해야한다는 규칙은 없습니다.

서로 다른 두 개의 파트를 각각 x축 기준 y축 기준으로 사용하면 됩니다.


group {
      parts {
         part {
            name: "to_part";
            type: RECT;
            scale: 1;
            description {
               state: "default" 0.0;
               color: 255 0 0 255;
               rel1.relative: 0.2 0.2;
               rel1.to:"bg";
               rel2.relative:0.8 0.8;
               rel2.to_x: "some_part";
               rel2.to_y: "other_part";
               }             }          }       }    }


위의 코드에서 시작점은 bg를 기준으로 설정되었지만,

끝점의 x축은 some_part를 기준으로,

y축은 other_part를 기준으로 위치를 잡습니다.

 

- offset: [x축] [y축];

offset은 음수, 0, 양수를 사용할 수 있습니다.

단위는 픽셀입니다.

여기서 사용하는 값은 relative와 to로 결정된 위치에 마지막 수정을 가합니다.

보통 미세한 조정이 필요한 경우에 사용합니다.


offset에 적용되는 값은 이전에 설명한 scale과는 무관하게 동작합니다.

scale이 1로 설정되어 있더라도,

offset에 설정한 값은 적힌 값 그대로 사용됩니다.


보통 선을 만들거나 1~2픽셀 단위로 영역을 조정할 때 사용합니다.

-100 이나 100 픽셀로 offset를 주진 않습니다.

저 정도 크기로 영역을 조정하려면 relative를 사용하는 것이 맞습니다.


collections {
   group {
      parts {
         part {
            name: "part_three";
            type: RECT;
            scale: 1;
            description {
               state: "default" 0.0;
               color: 255 0 0 255;
               rel1 {
                  relative: 0.0 0.0;
                  to:"some_part";
                  offset: 1 -1;
               }
               rel2 {
                  relative:0.0 1.0;
                  to:"other_part";
                  offset: 1 -1;
               }
            }
         }
      }
   } 


위치에 대한 이야기는 여기서 마치겠습니다.

그럼 좋은 하루 보내세요~

끝_

  1. Tizen Develer 2015.11.23 17:10

    상세한 설명 감사드립니다ㅎㅎ 초보 개발자인데 이렇게 상세하게 설명되어 있는 블로그는 처음봐요!!
    열심히 보고 배우겠습니다~

part 블록을 처음 접했을때,

description 블록의 역할이 와닿지 않았습니다.

part 블록에 바로 넣어도 될만한 필드들을 굳이 description으로 감싸기 때문이죠.


하지만, program이 description 단위로 동작한다는 것을 본 이후에는,

description 파트도 치열한 고민의 산물이란 것을 깨달았습니다.



안녕하세요, Tizen 개발자 윤진입니다.


description 파트는 program에 의해 변할 수 있는 요소들을 넣어줍니다.

description에서 정의하는 모든 필드는 상태에 따라 변하는 필드라고 보시면 됩니다.

description의 필드값을 program으로 직접 변경하진 않고,

한 part 내에 위치한 다수의 description 사이에서 상태변환을 하게 되지요.


따라서 description의 이름은 name이라 부르지 않고 state라고 합니다.

part의 확정된 설정값이 아닌,

유동적으로 변모하는 상태를 나타내기 때문입니다.


우선 description에서 다루는 필드르 아래 코드에서 훑어보겠습니다.

description {
   state: "description_name" INDEX;
   inherit: "other_description_name" INDEX;
   visible: 1;
   align: 0.5 0.5;
   min: 0 0;
   max: -1 -1;
   fixed: 0 0;
   rel1 { ... }
   rel2 { ... }
   step: 0 0;
   aspect: 1 1;
}

위에 언급된 필드 중에 rel1, rel2는 다음 포스팅에서 살펴보도록 하겠습니다.

rel 블록 내에는 또 주요한 필드들이 있고 해당 필드에 대해서는 할 말이 좀 있어서요.

그 외의 필드는 하나씩 살펴보도록 하겠습니다.



- state: [이름] [인덱스];

state의 이름은 고유한 값이어야 합니다.

그리고 state 중 하나는 반드시 "default"여야만 합니다.

"default"는 파트가 load될 때 initial 상태이기 때문이지요.

그리고 인덱스는 0.0에서 1.0의 값으로 입력합니다.

인덱스가 있는 이유는 상태가 동일한 이름을 지닌다고 해도 인덱스로 구분지을 수 있기 때문이죠.

위에 언급한 "default"의 0.0 인덱스가 가장 기본이 되는 description입니다.



- inherit: [다른 description의 이름] [다른 description의 인덱스];

다른 description에서 설정한 필드를 그대로 가져올 수 있습니다.

inherit한 이후에 설정한 필드들은 inherit된 필드에 override됩니다.

inherit는 필드 한 두개만 다르고 나머지 필드는 모두 일치할 때 유용하게 사용됩니다.



- visible: [0 혹은 1];

화면에 보일지 감출지를 결정합니다.

0을 설정하면 해당 파트는 보여지지 않습니다.

1을 설정해야 비로소 화면에 보입니다.

화면에 보이지 않으면 이벤트도 그 파트에는 전달되지 않습니다.

원래부터 없는 셈치게 되죠.

default 값은 1입니다.

따로 적어주지 않으면 화면에 보이게 됩니다.



- align: [X축] [Y축];

align으로 X축 혹은 Y축 기준으로 정렬을 할 수 있습니다.

align을 지정하면 이 파트에 삽입되는 다른 요소의 정렬에 적용됩니다.

이 파트 자체가 다른 곳에 삽입될 때 적용되는 것이 아닙니다.

0.0 ~ 1.0의 값을 지정할 수 있습니다.

0.0은 왼편 정렬, 0.5는 가운데 정렬, 1.0은 오른편 정렬입니다.

double 형이니 만큼 0.25, 0.3 같은 값을 주어 애매한 정렬을 할 수도 있겠죠.

default 값은 X축, Y축 모두 가운데 정렬인 0.5 0.5입니다.



- min: [width] [height];

min 필드는 두 개의 값을 가지고 있습니다.

가로와 세로를 기준으로 파트의 min 사이즈를 지정할 수 있습니다.

min은 말 그대로 최소한의 크기입니다.

파트의 크기는 min에서 설정한 값으로 확정됩니다.

min 값은 relative로 설정한 값과 충돌을 일으킬 수 있기 때문에,

가급적이면 relative를 '점'으로 설정한 곳에 사용해야 합니다.

이에 대해선 relative를 설명할 때 다시 언급하도록 하겠습니다.



- max: [width] [height];

max 필드도 두 개의 값을 가지고 있습니다.

가로와 세로를 기준으로 파트의 max 사이즈를 지정할 수 있습니다.

max는 말 그대로 최대한의 크기입니다.

파트의 크기가 변하는 상황에서 참조하는 값입니다.

파트의 크기가 바로 max 값으로 지정되진 않지요.



- fixed: [가로, 0 or 1] [세로, 0 or 1];

fixed는 매우 중요한 필드입니다.

여기에 어떤 값을 주었느냐에 다라 다른 그룹에 지대한 영향을 미칠 수 있습니다.

인자는 2개인데 각각 가로와 세로를 의미합니다.

가로 혹은 세로를 기준으로 현재 파트의 min값의 영향력을 결정할 수 있습니다.


만약 fixed가 1이라면,

현재 파트의 min값은 현재 파트를 담고 있는 객체에 아무 영향을 주지 못합니다.

현재 파트를 담고 있는 객체의 크기가 현재 파트보다 작더라도,

현재 파트를 담을 수 있습니다.

'담다'는 용어보다는 붙여놓는다는 용어가 어울릴지도 모르겠다.


반대로 fixed가 0이라면,

현재 파트의 min값은 현재 파트를 담고 있는 객체에 영향을 줍니다.

현재 파트가 100 x 100이고 현재 파트를 담는 객체가 10 x 10인 경우,

부모 객체는 현재 파트의 min 값에 맞춰서 100 x 100이 됩니다.


따라서 fixed를 사용하려면 부모/자식 같의 크기를 잘 염두하여 사용해주세요.

특히 파트 사이즈가 변한다면 더욱 주의하셔야 합니다.

사이즈가 변하기 전에는 부모 파트의 크기가 제대로 잡혀있었는데,

자식의 사이즈가 변한 후에는 부모 파트도 같이 변해서 애초의 목적과 달라질 수 있습니다.



- step: [width] [height];

part를 resize하는 경우, step에 설정한 가로/세로 크기만큼의 단계로 확대/축소됩니다.

만약 step이 필요없다면 "0 0"으로 설정해두면 됩니다.

개인적으로 step을 사용해본 경험이 없어서 타이젠 소스를 뒤져보았습니다.

타이젠 플랫폼에서도 step을 사용한 예는 거의 없네요.


         part { name: "e.table.days"; type: TABLE;
            description { state: "default" 0.0;
               rel1.to_y: "e.table.daynames";
               rel1.relative: 0.0 1.0;
               rel1.offset: 2 2;
               rel2.offset: -3 -3;
               step: 7 5;
               table {
                  homogeneous: TABLE;
                  padding: 1 1;
                  align: 0.5 0.5;
                  min: 1 1;
               }
            }
         /* ... 생략 ... */


위처럼 TABLE을 만드는 경우 step을 사용하고 있습니다.

TABLE의 크기를 변경할 때 step에 명시된 크기 7, 5만큼 변경되겠지요.


위에서 7, 5인 이유는 캘린더를 위한 테이블이기에,

월화수목금토일을 담기 위한 7열에, 총 5주를 표기하기 위해서인 것으로 보입니다.

부모의 크기가 커지더라도 캘린더 테이블 파트는 7, 5 단위씩 커져기에,

모두가 균일한 크기를 유지할 수 있도록 합니다.



- aspect: [가로, 0 or 1] [세로, 0 or 1];

파트의 사이즈를 재조정하는 경우,

가로/세로의 사이즈가 일정 비율로 늘거나 줄어야 하는 경우가 있습니다.

이미지의 경우에는 특히 그러하지요.

그럴 때, aspect를 사용합니다.

가로/세로의 값으로 1로 주면, 이미지는 비율대로 크기가 조정됩니다.

가로/세로를 각각 1 혹은 0, 0 혹은 1로 설정하여 사용한 경우는 보지 못했습니다.

가로/세로 모두 1 아니면 0을 사용하지요.


- aspect_preference: [DIMENSION];

aspect_preference는 aspect와 함께 사용합니다.

값으로는 BOTH, VERTICAL, HORIZONTAL, NONE을 넣어줄 수 있습니다.

여기서 설정한 Dimension으로 비율이 결정됩니다.

보통의 이미지라면 BOTH로 해도 무방합니다.

세로를 기준으로 가로의 사이즈를 조절하고 싶다면 VERTICAL을,

가로를 기준으로 세로의 사이즈를 조절하고 싶다면 HORIZONTAL을,

그리고 따로 기준이 필요없다면 NONE으로 설정합니다.



- color: [Red] [Green] [Blue] [Alpha];

RGB와 Alpha 값을 설정할 수 있습니다.

RGB값으로 색상을 정하고 알파값으로 투명도를 정할 수 있습니다.

각 값은 0~255까지로 지정합니다.

숫자가 높아질수록 각각의 색상이 점차 뚜렷해집니다.

알파값의 경우 0이면 투명, 255에 가까울수록 불투명해집니다.



오늘은 여기까지 하겠습니다.

그럼 좋은 하루 보내세요~

끝_


part의 영역 중 일부만 노출해야하는 경우가 있습니다.

포토샵에 마스킹을 생각하시면 됩니다.

자신이 지정한 영역만 보이고 그 외의 영역은 아예 렌더링도 하지 않습니다.

바로 그러한 기능이 필요할 때 clip_to를 사용합니다.


clip이 자르다 혹은 깎다는 의미가 있는데요,

clip to로 지정한 영역 외에는 잘라버려서 보이지 않는다고 보면 됩니다.



안녕하세요, Tizen 개발자 윤진입니다.


clip_to: [another part's name];

clip_to는 파트블록 내에서 사용합니다.

필드값으로 다른 파트의 이름을 적어주어야 합니다.

현 파트의 영역 중 필드값으로 지정한 파트의 영역와 겹치는 부분만 렌더링하게 됩니다.


우선, 간단하게 이미지를 화면에 그려보도록 하겠습니다.

images {
  image: "bg.jpg" COMP;
}
collections {
  group {
    name: "storycompiler";
    parts {
      part {
        name: "bg";
        type: RECT;
        description {
          state: "default" 0.0;
          rel1.relative: 0.0 0.0;
          rel2.relative: 1.0 1.0;
          color: 0 0 0 255;
          visible: 1;
        }
      }
      part {
        name: "img";
        type: IMAGE;
        scale: 0;
        description {
          state: "default" 0.0;
          image.normal: "bg.jpg";
          rel1 { relative: 0.5 0.5; to: "bg"; }
          rel2 { relative: 0.5 0.5; to: "bg"; }
          min: 1145 425;
          fixed: 1 1;
          align: 0.5 0.5;
          visible: 1;
        }
      }
    }
  }
}


위의 코드에 있는 collections, group, parts, part 블록은 이미 지난 포스팅에서 설명을 드렸습니다.

image 타입에 대해서 아직 다루진 않았지만,

최소한의 기능만을 사용하였기에 코드만 봐도 이해에 어려움이 없으리라 생각합니다.


IMAGE 타입을 가진 img 파트는 bg 파트의 정중앙(0.5 0.5)에 위치하는 '점'입니다.

하지만, min값을 가지고 있기 때문에 min값만큼 영역이 늘어나게 됩니다.

그래서 나온 결과는 위의 캡쳐이미지와 같습니다.


코드에는 bg와 img 파트만 있는데요,

clip_to를 테스트하기 위하여 가운데 점으로부터 100 x 100짜리 사각형을 하나 만들어보겠습니다.


      part {
        name: "clip";
        type: RECT;
        description {
          state: "default" 0.0;
          rel1 { relative: 0.5 0.5; to: "bg"; }
          rel2 { relative: 0.5 0.5; to: "bg"; }
          color: 255 0 0 255;
          min: 100 100;
          align: 0.5 0.5;
          visible: 1;
        }
      }


위의 파트까지 포함하여 출력하면 아래 이미지를 볼 수 있습니다.

clip 사격형의 color는 RGBA 기준으로 255 0 0 0으로 붉게 두었습니다.


          color: 255 0 0 255;

자, 이제 clip_to를 위한 준비를 마쳤습니다.

img 파트에 clip_to 필드를 추가합니다.


      part {
        name: "img";
        type: IMAGE;
        scale: 0;
        clip_to: "clip";
        description {
          state: "default" 0.0;
          image.normal: "bg.jpg";
          rel1 { relative: 0.5 0.5; to: "bg"; }
          rel2 { relative: 0.5 0.5; to: "bg"; }
          min: 1145 425;
          fixed: 1 1;
          align: 0.5 0.5;
          visible: 1;
        }
      }


위처럼 한 줄 추가하면 굉장히 극적인 변화가 일어납니다.

clip 파트는 clip_to의 대상이 되기 때문에 img파트를 위한 '체'만 제공할 뿐입니다.

clip 파트가 '붉은색' 사각형이었기 때문에 '붉은 체'가 되겠지요.


img파트는 clip파트 영역만큼만 화면에 보이게 됩니다.

clip 파트가 '붉은 체'이기 때문에 붉게 물든 이미지가 보이겠네요.

그 외의 영역은 렌더링을 하지 않겠지요.

img보다 뒷편에 있는 bg의 검은 영역만 보이겠죠.



이전 이미지에서는 붉은 색 사각형에 가려져 보이지 않던 영역이 보이는 것을 확인할 수 있습니다.

붉은 색 사각형 영역만큼만 보이고 있습니다.


clip 영역에 애니메이션을 주면,

여러가지 재미난 효과를 추가할 수도 있습니다.


오늘은 여기까지 하겠습니다.

그럼 좋은 하루 보내세요~

끝_


edc의 part에 들어가는 필드 중에 ignore_flags가 있습니다.

무언가를 무시하기 위한 목적으로 만들어진 필드일텐데요,

이름만 봐서는 역할이 분명하게 다가오지 않네요.


이럴 때는 구글링으로 궁금증을 해소하거나,

EFL 소스를 뒤져보거나,

시간과 노력을 들여 순수한 삽질로 기능을 유추해야 합니다.

아니면, EFL 커미터에게 헬프를 외치면 됩니다.



안녕하세요, Tizen 개발자 윤진입니다.


우선, EFL 공식 사이트에서 설명을 찾아보겠습니다.

"Specifies whether events with the given flags should be ignored,

i.e., will not have the signals emitted to the parts."


플래그에 대한 이야기가 나옵니다.

플래그를 설정하면 이벤트가 무시될 수 있다고 하네요.

하지만, 단순히 ignore_flags에 넣는 값에 따라 이벤트를 무시한다면,

이전 포스팅에서 다룬 mouse_events와 다를 바가 없겠지요.


ignore_flags는 mouse_events처럼 무조건적으로 이벤트를 통제하는 것이 아닙니다.

경우에 따라 이벤트를 막거나 혹은 받을 수 있습니다.

그렇다면 그 '경우에 따라'는 어디에서 기인하는 것일까요?


파트 자신이 스스로 각각의 경우를 만들어낼 수 있을까요?

아닙니다. ignore_flags를 적어놓고 있는 파트 자신은 스스로 각각의 경우를 발생하지 못합니다.

대신 위의 파트와 관련이 있는 다른 파트들이 각각의 경우를 만들어냅니다.


어째서 이러한 구조가 필요한 것일까요?

스크롤이 가능한 영역을 생각해보지요.

스크롤이 가능한 영역에 다수의 객체가 삽입되었습니다.

스크롤을 하기 위해 press를 한 후 스크롤을 좌우로 움직입니다.

그리고 release를 하면 스크롤 내에 있던 객체는 이벤트를 받아야할까요?


분명 사용자는 스크롤을 목적으로 press - move - release를 했는데,

사용자의 의도와는 다르게 스크롤 내에 삽입된 객체가 release에 대한 이벤트를 처리할 수 있습니다.

사실 삽입객체는 이벤트를 ignore해야하는데 말이지요.


바로 여기에 ignore_flags 필드가 필요한 겁니다.

삽입객체에 "ignore_flags: ON_HOLD;"를 지정하면,

누군가가 ON_HOLD라고 외치는 순간 자신에게 오는 모든 이벤트는 무시하게 됩니다.

스크롤 와중에 삽입객체들은 이벤트를 받으면 안되지요.


이 사실을 파악하기 이해 먼저 evas 소스를 뒤져보았습니다.

Repository : git://review.tizen.org/framework/uifw/evas

Branch : tizen_2.4


/**
 * @brief Enumeration for events.
 */
typedef enum _Evas_Event_Flags
{
   EVAS_EVENT_FLAG_NONE = 0, /**< No fancy flags set */
   EVAS_EVENT_FLAG_ON_HOLD = (1 << 0), /**< The event is being delivered but should be put "on hold" until the on hold flag is unset \n
   The event should be used for informational purposes and maybe for some indications visually, but should not actually perform anything. */
   EVAS_EVENT_FLAG_ON_SCROLL = (1 << 1) /**< The event occurs while scrolling \n
   For example, DOWN event occurs during scrolling; the event should be used for informational purposes and maybe for some indications visually, but should not actually perform anything. */
} Evas_Event_Flags; /**< Flags for Events */


Evas 단에서 이미 ON_HOLD에 대한 enum을 정의하고 있습니다.

EVAS_EVENT_FLAG_ON_HOLD가 설정된 객체(Evas_Object 혹은 group 혹은 part)는,

타 객체에서 설정한 EVAS_EVENT_FLAG_ON_HOLD에 의해 이벤트를 받지 못합니다.


elementary 소스를 뒤져보면,

대부분의 위젯들이 event를 처리하는 콜백에서 해당 객체가 ON_HOLD 처리가 되어 있는지 확인하고,

ON_HOLD가 set되어 있으면 event 콜백 진입 직후에 return 해버립니다.


Repository : git://review.tizen.org/framework/uifw/elementary

Branch : tizen_2.4

./elm_video.c:31:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_flip.c:1541:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
./elm_flip.c:1572:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
./elm_flip.c:1634:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
./elm_scroller.c:127:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elc_popup.c:2148:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_flipselector.c:440:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_slideshow.c:34:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elc_multibuttonentry.c:80:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_gengrid.c:852:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
./elm_gengrid.c:2312:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_diskselector.c:913:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_web.c:180:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_entry.c:3208:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
./elm_button.c:297:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_interface_scrollable.c:2143:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
./elm_map.c:4378:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_panel.c:818:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elc_ctxpopup.c:1198:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_genlist.c:2682:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_genlist.c:3547:   //if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
./elm_radio.c:164:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./els_scroller.c:1863:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return ;
./els_scroller.c:1931:   //   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return ;
./els_scroller.c:2193:   //   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return ;
./els_scroller.c:2512:   //   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return ;
./elm_list.c:426:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_colorselector.c:1760:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_slider.c:381:   if (mev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_slider.c:391:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elc_naviframe.c:1332:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
./elc_naviframe.c:1773:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_check.c:155:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elc_player.c:51:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_toolbar.c:756:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
./elm_photocam.c:944:   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;


스크롤러 등에 삽입되어 이벤트를 유연하게 처리하고자 한다면,

위의 위젯들처럼 ON_HOLD가 설정된 event에는 return 처리를 해야겠지요.


그렇다면, 최초 발생한 이벤트는 어떻게 되는 것일까요?

ON_HOLD된 파트들은 건너뛰고,

계속 부모 객체를 찾아갑니다.

ON_HOLD가 설정되지 않은 부모 객체까지 타고 올라가서 이벤트를 처리하게 합니다.

그와 관련된 부분은 아래 propagate 함수에서 엿보실 수 있습니다.


Repository : git://review.tizen.org/framework/uifw/elementary

Branch : tizen_2.4

src/lib/elm_widget.c

EAPI Eina_Bool
elm_widget_event_propagate(Evas_Object *obj,
                           Evas_Callback_Type type,
                           void *event_info,
                           Evas_Event_Flags *event_flags)
{
   API_ENTRY return EINA_FALSE; //TODO reduce.

   if (!_elm_widget_is(obj)) return EINA_FALSE;
   Evas_Object *parent = obj;
   Elm_Event_Cb_Data *ecd;
   Eina_List *l, *l_prev;

   while (parent &&
          (!(event_flags && ((*event_flags) & EVAS_EVENT_FLAG_ON_HOLD))))
     {   
        sd = evas_object_smart_data_get(parent);
        if ((!sd) || (!_elm_widget_is(obj)))
          return EINA_FALSE;  //Not Elm Widget
        if (!sd->api) return EINA_FALSE;

        if (sd->api->event(parent, obj, type, event_info))
          return EINA_TRUE;

        EINA_LIST_FOREACH_SAFE(sd->event_cb, l, l_prev, ecd)
          {https://docs.enlightenment.org/auto/edje/edcref.html
             if (ecd->func((void *)ecd->data, parent, obj, type, event_info) ||
                 (event_flags && ((*event_flags) & EVAS_EVENT_FLAG_ON_HOLD)))
               return EINA_TRUE;
          }
        parent = sd->parent_obj;
     }   

   return EINA_FALSE;
}


이상입니다.

다음 포스팅에서도 part의 기본필드를 알아보도록 하겠습니다.


그럼 좋은 하루 보내세요~

끝_


* References

https://docs.enlightenment.org/auto/edje/edcref.html


part 블록에서는 자칫 정신을 잃으면, 급류에 휩쓸려 내려가 죽습니다;

워낙에 많은 복병과 암초가 기다리고 있기 때문에,

가급적이면 많은 예외사항을 다뤄보도록 하겠습니다.

만약 누락된 것이 있다면 댓글로 남겨주세요.

힘 닿는 만큼 추가해보도록 하겠습니다.



안녕하세요, Tizen 개발자 윤진입니다.


part는 parts 블록 안에 위치합니다.

parts 블록 안에는 1개 이상의 part를 배치할 수 있습니다.

parts 안에는 part 말고 다른 블록은 없지요.


parts 안에 쌓이는 part의 순서에도 의미가 있습니다.

위에 있는 part 일수록 레이어는 가장 아래에 위치합니다.

하나씩 part를 만들때 가장 상단에 있는 part부터 차곡차곡 쌓는다고 생각하면 됩니다.



개별적인 part는 완결된 화면 구성 단위인 group 내에 위치한 구성 '요소'입니다.

뒤집에 말하면, 각각의 구성요소가 모여서 하나의 의미있는 group이 되지요.


group {
   parts {
      part{} /* 최하단 레이어 */
      part{}
      part{}
      part{}
      part{}
      part{} /* 최상단 레이어 */
   }
}


part는 선이나 사각형이 될 수 있고,

이미지나 텍스트가 될 수도 있습니다.

버튼과 같은 다른 group을 통째로 넣을 수도 있습니다.

각각의 쓰임에 따라 syntax가 조금씩 달라집니다.


group {
   parts {
      ...
      part {
         name: "part_name";
         type: IMAGE;
         mouse_events:  1;
         repeat_events: 0;
         ignore_flags: NONE;
         clip_to: "another_part";
         source:  "group_name";
         pointer_mode: AUTOGRAB;
         description { }
         dragable { }
         items { }
      }
      ...
   }
}


part 내에서 사용하는 기본적인 필드를 우선 살펴보겠습니다.

여기서 언급하는 필드로 파트의 속성을 엿볼 수 있습니다.

필드의 세부적인 속성값이나 여기에 언급하지 않은 필드도 다른 포스팅에서 다룰 예정입니다.

한 번에 모두를 다루기에는 그 양이 너무 많네요.


- name: [part_name];

   파트의 이름을 지정할 수 있습니다.

   파트 이름은 program에서 애니메이션 효과를 줄 때 사용할 수 있습니다.

   c코드에서 파트이름으로 파트에 직접 접근할 수도 있습니다만 일반적인 용례는 아닙니다.


   어쨌든 외부에서 접근할 수 있으니,

   다른 파트 이름과 구별되는 고유의 이름을 지어주어야 합니다.

   파트이름은 큰 따옴표로 감싸주세요.



- type: [TYPE];

   타입은 아래처럼 확정된 값을 따옴표 없이 대문자로 넣을 수 있습니다.

   만약 아무런 값도 지정하지 않았다면 default로 IMAGE가 설정됩니다.

•    TEXT : 싱글라인 텍스트
•    TEXTBLOCK : 멀티라인/스타일이 가능한 텍스트블록
•    RECT : 사각형
•    IMAGE : 이미지
•    SWALLOW : group을 통째로 탑재할 수 있는 SWALLOW 타입(c에서 확정)
•    GROUP : group을 통째로 탑재할 수 있는 GROUP 타입(edc에서 확정)

•    BOX : 박스에 다른 그룹을 차곡차곡 삽입
•    TABLE : 테이블

•    EXTERNAL : 익스터널 오브젝트
•    PROXY : 파트 컨텐트 재사용(속도 최적화)
•    SPACER : 파트 위치+크기 지정(메모리 최적화)



- mouse_events:  [1 or 0];

   이름은 마우스 이벤트이지만 파트영역에서 발생하는 모든 이벤트를 통제할 수 있습니다.

   설정할 수 있는 값은 True를 뜻하는 1과 False를 뜻하는 0입니다.

   True를 설정하면 해당 파트가 '이벤트를 받을 수 있는 상황에서' 이벤트를 받게 됩니다.


   이벤트를 받을 수 있는 상황이란,

   - 파트영역이 화면에 보여야하고,

   - 파트영역을 덮는 다른 파트가 있으면 안됩니다.


   흔히 저지르는 실수 중에 하나가 투명한 파트를 상위 레이어에 만들어 두고,

   그 아래에 있는 파트에서 이벤트를 기다리는 것이지요.

   아무리 기다려도 위에 있는 레이어가 낚아채버리면 이벤트는 오지 않습니다.

   가장 기본적인 원칙이지요.


   만약 0으로 설정하면 어떤 이벤트도 받지 않습니다.

   이벤트를 받을 수 있는 상황인데 이벤트를 받지 않으면,

   그 하위에 있는 레이어에게 기회가 돌아가게 됩니다.

   역으로 자기보다 상단에 레이어가 있지만,

   상단 레이어가 mouse_events를 0으로 설정하면 자기가 이벤트를 받을 수 있습니다.

   default 값은 이벤트를 처리한다는 뜻으로 1로 설정하지요.



- repeat_events: [1 or 0];

   위에서도 언급하였지만 이벤트는 레이어의 높낮이에 영향을 받습니다.

   최상위 레이어부터 최하위 레이어까지 이벤트가 순서대로 전달될 수 있습니다.

   만약 상위 레이어가 이벤트를 처리하였지만, 하위레이어도 이벤트를 받아야하는 경우도 있을 수 있습니다.

   그럴 때 repeat_events를 사용하여 이벤트를 하위 레이어에 전달할 수도 있습니다.

   default는 0이기 때문에 이벤트를 받은 레이어 아래에 이벤트를 전달하지 않습니다.



- scale: [1 or 0];

   scale은 매우 중요한 개념이지만 그 중요도만큼이나 까다로운 필드입니다.

   scale 값을 True인 1로 설정하면, scale factor의 영향을 받는다는 의미이고,

   False인 0으로 설정하면, scale factor의 영향을 받지 않는다는 의미입니다.


   scale factor는 무엇일까요?

   scale factor는 double 형의 상수입니다.

   해상도와 화면사이즈가 제각각인 디스플레이에서,

   육안으로 봤을때 유사한 크기로 교정하기 위해 플랫폼에서 제공하는 계산된 인자입니다.


   예를 들어 동일한 해상도의 1인치/3인치 디스플레이가 있다고 합시다.

   1인치 화면에서 1cm 너비로 보이는 파트영역을,

   3인치 화면에서 3cm가 아닌 여전히 1cm로 너비를 지정하고 싶을 경우,

   1인치에서 1로 scale factor를 지정하였다면,

   3인치에서는 1/3로 scale factor를 지정합니다.

   그러면 1인치에서는 1cm x 1scale factor = 1cm로 설정이 되고,

   3인치에서는 3cm x 1/3scale factor = 1cm로 설정이 되겠지요.


   굉장히 간단하게 설명을 드렸지만, 대강의 의미는 전달되었을거라 생각합니다.

   차후에 scale factor에 대한 이야기를 제대로 할 기회가 있겠지요.


   이러한 scale factor에 영향을 받는 값은,

   min, max 그리고 font size입니다.

   위의 세값을 설정하지 않은 파트는 scale을 1로 지정하여도 scale의 영향을 받지 않습니다.

   반대로 min, max, font size를 지정한 파트에 scale 값을 0으로 줘버리면,

   scale factor의 영향을 받지 않고 지정한 사이즈 그대로 화면에 보여집니다.

  

   default값은 1.0입니다.

   스케일 값을 사용하는 것이 default이지요.



- pointer_mode: [MODE];

   파트영역 내에서 시작하였지만, 파트영역 밖에서 끝난 이벤트를 처리하는 방식을 지정할 수 있습니다.

   mouse down과 mouse up 이벤트로 설명하는 것이 빠르겠네요.

   파트영역 내에서 mouse down을 하였지만,

   mouse를 move하여 파트영역 밖으로 빠져나간 후 mouse up을 할 수 있습니다.

   그렇게 되면 mouse up이벤트를 파트영역이 받아야 할까요?

   그에 대한 선택을 사용자가 pointer_mode로 할 수 있습니다.


   AUTOGRAB 값을 주면,

   mouse up 이벤트가 파트영역 외부에서 이뤄지더라도 mouse down이 발생한 파트가 이벤트를 받습니다.


   NOGRAB 값을 주면,

   mouse up 이벤트가 파트영역 외부에서 이뤄지면 mouse down이 발생한 파트로는 이벤트가 오지 않습니다.


   default는 AUTOGRAB이기 때문에 NOGRAB을 사용하지 않는 이상,

   pointer_mode를 굳이 사용할 필요는 없습니다.



- precise_is_inside: [1 or 0];

   투명한 영역을 가지고 있는 이미지에서 빛을 발하는 '훌륭한' 기능입니다.

   이미지가 이벤트를 받는 경우,

   이미지의 투명한 영역은 이벤트를 받지 않고,

   불투명한 영역에서만 이벤트를 받고 싶을 수도 있습니다.

   바로 그럴 경우, precise_is_inside를 1로 지정하면 됩니다.

   그러면 투명한 영역은 파트영역 내부(inside)로 포함되지 않습니다.


   default 값은 0입니다.

   투명한 영역도 이미지의 영역이므로 이벤트를 모두 처리합니다.


오늘은 여기까지 하겠습니다.

part가 가지는 다른 무수한 기능은 다음 포스팅에서 하나씩 살펴보겠습니다.


그럼 좋은 하루 보내세요~

끝_


edc에서 group은 비교적 군더더기 없이 만들어져 있습니다.

group을 사용할 때, '충돌'이나 '이해할 수 없는 동작'을 고민할 필요가 없습니다.


group과 얽힌 재미난 에러를 하나쯤 언급하기 위해 기억을 더듬고 있지만,

도통 떠오르지 않네요.

비교적 간단한 역할을 담당하고 있으니 실구현에도 어려움은 없는 녀석이었나봅니다.

여러분도 그럴 것이라 믿어의심치 않습니다. :)



안녕하세요, Tizen 개발자 윤진입니다.


edc를 작성할 때 group은 하나의 완결된 구성이 됩니다.

c파일에서 elm_layout_add(), elm_layout_file_set() 콤보로 가져다 쓰는 단위가 group입니다.

하나의 collections에 수많은 group이 있다면,

c에서 필요한 group만큼 elm_layout_add()를 불러서 사용하면 됩니다.


그렇다면 완결된 구성을 위해서는 그 안에 어떤 요소를 담고 있어야 할까요?

그룹이 가진 많은 요소 중에 가장 중요한 요소는 parts와 programs 두가지입니다.

parts는 화면을 구성하는 part의 상위 블록이고,

programs는 part에 움직임을 부여할 수 있는 program의 상위 블록이지요.


collections {
   group {
      parts {
         part{}
         part{}
         part{}
         part{}
      }
      programs {
         program{}
         program{}
         program{}
         program{}
      }
   }


지난
collections 포스팅에서도 언급한 블록도에서 programs를 추가해봤습니다.

위의 블록도처럼 group에 parts와 programs만 있으면 화면에 무언가 그려낼 수 있습니다.

parts와 programs 블록은 각각 며칠씩 공을 들여 설명해보도록 하겠습니다.


group에는 parts와 programs 외에 다른 요소들도 있습니다.

name, inherit, min, max.

위의 요소들은 parts와 programs에 비하면 아주 간단하게 설명할 수 있습니다.


- name: [group name];

   그룹의 이름을 지정합니다.

   위에서 잠깐 언급한 elm_layout_file_set()에서 세번째 인자가 그룹 name입니다.

   c파일에서는 group이 있는 파일명과 여기서 지정한 그룹명으로 그룹에 접근할 수 있습니다.

   만약 group 이름이 동일하다면 새로운 group이 이전 group을 완전히 override해버립니다.

   edc에서 group override를 의미있게 사용하는 것을 보질 못해서,

   이 부분은 컴파일을 위한 편의가 아닐까 추정하고 있습니다.

   어쨌든, override되지 않게 그리고 문맥에 맞게 사용하려면 group명을 잘 지어주세요.

   언제나 모든 것은 적절한 네이밍에서 시작하지요.


- min: [width] [height];

   min의 의미는 "최소한 이 사이즈를 확보해주세요"입니다.

   group이 어디에 포함되어 들어가든 min 사이즈만큼은 확보가 됩니다.

   min에서 지정한 사이즈보다 group이 작아질 수는 없습니다.

   원칙적으로 그렇습니다.

   min에서는 그룹의 높이와 너비를 지정하겠지요.

   그룹의 높이/너비가 확정되어 있고 '절대로 변할 일이 없다'면,

   group 차원에서 정의해도 무방합니다.

   하지만, group이 상태에 따라 사이즈가 바뀐다면,

   여기서 min을 사용하면 안됩니다.

   min은 mandatory가 아니기에 상황에 따라 사용하면 됩니다.

   참고로 전 group에서 min을 사용하지 않습니다.

   min은 다른 곳에서 더 재미있는 형태로 사용할 수 있습니다.


- max: [width] [height];

   max는 min과는 반대의 개념입니다.

   "최대한 이 사이즈를 넘지 않도록 해주세요" 입니다.

   max의 중요성은 min에 비해 상당히 떨어지는데요,

   각각의 요소들이 사이즈를 잡을때 min을 기준으로 잡기 때문입니다.

   max는 느슨한 가이드라인이지요.

   그렇다고 사용하지 않는다는 것은 절대 아닙니다.

   차후에 min/max에 대해서는 계속 설명할 기회가 있을 겁니다.

   어쨌든 그룹이 max 값을 넘지 않게 합니다.

   min처럼 mandatory가 아니기 때문에 필요에 의해 사용하면 됩니다.


- alias: [additional group name];

   alias에서 그룹에 별칭을 지정할 수 있습니다.

   하나의 그룹이 다수의 이름을 가질 필요가 있을때 사용합니다.

   그룹을 하나 만들었는데 여러 군데에서 사용할 수 있고,

   각각에 고유한 네이밍을 하길 원할 때가 있습니다.

   그 때 alias는 의미있는 선택이 될 수 있습니다.


group의 모든 요소를 골고루 사용하는 define문을 icon.edc에서 찾았습니다.

Repository : git://review.tizen.org/framework/uifw/elementary

Branch : tizen_2.3

File : data/themes/widgets/icon.edc


#define GROUP_ALIAS_ICON(Name, Alias, File, Min, Max)                   \
   group { name: "elm/icon/"##Name##"/default"; min: Min Min; max: Max Max; \
      images.image: File COMP; parts { part { name: "base";             \
            description { aspect: 1.0 1.0; aspect_preference: BOTH;     \
               image.normal: File; } } } }
/*** 생략 ***/
GROUP_ALIAS_ICON("home", "toolbar/home", "icon_home.png", 32, 0);
GROUP_ALIAS_ICON("close", "toolbar/close", "icon_close.png", 32, 0);
GROUP_ALIAS_ICON("apps", "toolbar/apps", "icon_apps.png", 32, 0);
GROUP_ALIAS_ICON("arrow_up", "toolbar/arrow_up", "icon_arrow_up.png", 32, 0);
GROUP_ALIAS_ICON("arrow_left", "toolbar/arrow_left", "icon_arrow_left.png", 32, 0);
GROUP_ALIAS_ICON("arrow_right", "toolbar/arrow_right", "icon_arrow_right.png", 32, 0);
GROUP_ALIAS_ICON("chat", "toolbar/chat", "icon_chat.png", 32, 0);
GROUP_ALIAS_ICON("clock", "toolbar/clock", "icon_clock.png", 32, 0);
GROUP_ALIAS_ICON("delete", "toolbar/delete", "icon_delete.png", 32, 0);
GROUP_ALIAS_ICON("edit", "toolbar/edit", "icon_edit.png", 32, 0);
GROUP_ALIAS_ICON("refresh", "toolbar/refresh", "icon_refresh.png", 32, 0);
GROUP_ALIAS_ICON("folder", "toolbar/folder", "icon_folder.png", 32, 0);
GROUP_ALIAS_ICON("file", "toolbar/file", "icon_file.png", 32, 0);
GROUP_ALIAS_ICON("no_photo", "photo/no_photo", "head.png", 32, 0);


위에서 난데없는 #define이 나와서 당황하셨을 지도 모르겠군요.

C언어에서처럼 #define을 유사하게 사용할 수 있습니다.

우리가 살펴봐야할 부분은 GROUP_ALIAS_ICON의 Name, Alias, Min, Max입니다.

실제로 #define이 사용되는 부분을 보면,

Name, Alias, Min값을 주로 사용하고 있군요.

Max는 마지막 인자로 '0'을 넣어주고 있네요.

최대 크기가 0이라는 의미는 아니고 Max를 사용하지 않겠다는 의미입니다.

이 예시에서는 그룹의 Name과 Alias를 지정하고,

최소/최대 사이즈를 지정하였습니다.


group에 대한 설명은 이것으로 간단하게 마치겠습니다.

다음 시간에는 part에 대해서 살펴보도록 하겠습니다.


그럼 좋은 하루 보내세요~

끝_



EFL에 대해 블로깅을 하는 날이 오고야 말았습니다.

오늘부터 n일 동안 edc에 대해서 하나씩 살펴보도록 하겠습니다.


진도를 빨리 뺀다는 마음보다는,

다소 느리더라도 천천히 하나하나 맛을 음미하며 진행하고 싶네요.


하지만, 충분히 설명해낼 수 있을지는 확신이 안 서네요.



안녕하세요, Tizen 개발자 윤진입니다.


edc는 EFL의 백미라 할 수 있습니다.

(써 놓고 보니 '백미' 타이틀은 ecore나 evas에게 주어야하지 않나라는 생각도 듭니다;)

edc 스크립트를 사용하여 레이아웃을 잡고 이미지를 배치하고 텍스트를 심습니다.

이러한 정적인 배치는 edc의 part를 이용하여 구성할 수 있습니다.


위에 나열한 정적인 요소에 program을 통해 생명력을 불어넣을 수 있습니다.

part 하나 하나가 program의 손길로 동적인 효과를 가집니다.

네다섯줄의 스크립팅으로 부드러운 애니메이션을 탑재할 수 있습니다.


edc 스크립트에서 가장 처음 마주하게 되는 블록이 collections입니다.

collections에는 하나의 유의미한 단위로 동작하는 group이 들어갑니다.

group 화면을 구성하는 완결된 하나의 집합이고,

collections는 그러한 group을 1개 이상 가집니다.


예를 들어 버튼+텍스트의 집합을 하나의 완결된 'button' group이 있다고 하죠.

경우에 따라서 버튼의 모양은 천양지차로 다를 수 있습니다.

네모난 버튼이 일반적이긴 하지만, 사각이 라운딩 처리된 라운딩 버튼이 있을 수도 있습니다.

이럴 경우 네모난 버튼용 group과 라운딩 버튼용 group이 따로 둘 수 있습니다.

세모난 버튼용 group이나 투명 버튼용 group도 있을 수 있겠네요.

생김새는 조금씩 다르지만 버튼이라는 기능적인 유사성이 있기 때문에,

하나의 collections로 모아서 관리할 수 있겠죠.

이처럼 collections는 다양한 theme을 묶어주는 역할을 할 수 있습니다.


그렇다면 group이 하나만 있어도 collections로 묶어줘야 할까요?

네, 맞습니다.

collections는 group이 1개 이상 있는 경우에 반드시 써주어야 합니다.

collections로 감싸지지 않은 group은 제대로 컴파일되지 않습니다.

collections - group - parts - part

혹은

Collections - group - programs - program

위에 이르는 블록 구조에서 최상단에는 언제나 collections가 있습니다.


edc 파일은 collections를 반드시 갖지만,

두 개 이상 가질 수는 없습니다.

edc 파일에는 오직 하나의 collections만 사용할 수 있습니다.


collections {
   group {
      parts {
         part{}
         part{}
         part{}
         part{}
      }
   }


EFL elementary repository에서 사용하고 있는 edc 파일을 예제로 살펴보겠습니다.

Repository : git://review.tizen.org/framework/uifw/elementary

Branch : tizen_2.3

File : data/themes/default.edc

collections {
///////////////////////////////////////////////////////////////////////////////
   group { name: "elm/focus_highlight/top/default";
   /* ... 생략 ... */
      parts {
         part { name: "base";
            type: RECT;
            repeat_events: 1;
            description { state: "default" 0.0;
               rel1.relative: 0.0 0.0;
               rel2.relative: 1.0 1.0;
               visible: 0;
            }
         }
         part { name: "shine";
            type: IMAGE;
            mouse_events: 1;
            repeat_events: 1;
            ignore_flags: ON_HOLD;
            description { state: "default" 0.0;
               image {
                  normal: "emo-unhappy.png";
               }
               rel1.to: "base";
               rel1.relative: 1.0 0.0;
               rel1.offset: -15 -15;
               rel2.to: "base";
               rel2.relative: 1.0 0.0;
               rel2.offset: 14 14;
            }
            description { state: "disabled" 0.0;
               inherit:  "default" 0.0;
               color: 0 0 0 0;
            }
         }
   /* ... 생략 ... */
///////////////////////////////////////////////////////////////////////////////
   group { name: "elm/focus_highlight/bottom/default";
      parts {
         part { name: "shine";
            type: RECT;
            mouse_events: 1;
            repeat_events: 1;
            ignore_flags: ON_HOLD;
            description { state: "default" 0.0;
               color: 0 255 0 50;
               rel1.offset: 0 0;
               rel2.offset: 0 0;
            }
            description { state: "disabled" 0.0;
               inherit:  "default" 0.0;
               color: 0 0 0 0;
            }
         } 
   /* ... 생략 ... */
///////////////////////////////////////////////////////////////////////////////
   group { name: "elm/access/base/default";
      images {
         image: "access_glow.png" LOSSY 85;
      }
      parts {
         part { name: "block1"; type: RECT;
            mouse_events:  0;
            description { state: "default" 0.0;
               color: 0 0 0 200;
               rel1 { to: "base";
                  relative: 0.0 0.0;
                  offset: -100000 -100000;
               }
               rel2 { to: "base";
                  relative: 1.0 0.0;
                  offset: 100000 96;
               }
            }
         }
         part { name: "block2"; type: RECT;
            mouse_events:  0;
            description { state: "default" 0.0;
               color: 0 0 0 200;
               rel1 { to: "base";
                  relative: 0.0 1.0;
                  offset: -100000 -97;
               }
               rel2 { to: "base";
                  relative: 1.0 1.0;
                  offset: 100000 100000;
               }
            }
         }
         part { name: "block3"; type: RECT;
            mouse_events:  0;
            description { state: "default" 0.0;
               color: 0 0 0 200;
               rel1 { to: "block1";
                  relative: 0.0 1.0;
                  offset: 0 0;
               }
               rel2 { to_x: "base"; to_y: "block2";
                  relative: 0.0 0.0;
                  offset: 96 -1;
               }
            }
         }
         part { name: "block4"; type: RECT;
            mouse_events:  0;
            description { state: "default" 0.0;
               color: 0 0 0 200;
               rel1 { to_x: "base"; to_y: "block1";
                  relative: 1.0 1.0;
                  offset: -97 0;
               }
               rel2 { to: "block2";
                  relative: 1.0 0.0;
                  offset: -1 -1;
               }
            }
         }
         part { name: "base";
            mouse_events:  0;
            description { state: "default" 0.0;
               image.normal: "access_glow.png";
               image.border: 112 112 112 112;
               rel1.offset: -102 -102;
               rel2.offset:  101  101;
               fill.smooth: 0;
            }
         }
      }
   }


위에서 보시다시피 하나의 collections 아래에 다수의 group이 놓여져 있습니다.

focus와 highlight와 관련된 기능이 group으로 구현되어 있네요.


group이 더욱 많이지면 collections에다가 제대로 붙이기 힘들 정도로 길어질 수도 있습니다.

이럴 때는 collections 문 안에서 #include 문을 사용하여 group을 include할 수 있습니다.

같은 파일의 아랫 부분을 보면, #include 문을 사용했습니다.


collections {
   /* ... 생략 ... */
#include "widgets/bg.edc"
#include "widgets/border.edc"
#include "widgets/scroller.edc"
#include "widgets/label.edc"
#include "widgets/button.edc"
#include "widgets/clock.edc"
#include "widgets/datetime.edc"
#include "widgets/dayselector.edc"
#include "widgets/menu.edc"
#include "widgets/frame.edc"
#include "widgets/tooltip.edc"
#include "widgets/hover.edc"
#include "widgets/ctxpopup.edc"
#include "widgets/entry.edc"
#include "widgets/bubble.edc"
#include "widgets/photo.edc"
#include "widgets/thumb.edc"
#include "widgets/icon.edc"
#include "widgets/toolbar.edc"
#include "widgets/notify.edc"
#include "widgets/slideshow.edc"
#include "widgets/win.edc"
#include "widgets/list.edc"
#include "widgets/slider.edc"
#include "widgets/actionslider.edc"
#include "widgets/genlist.edc"
#include "widgets/check.edc"
#include "widgets/radio.edc"
#include "widgets/progressbar.edc"
#include "widgets/separator.edc"
#include "widgets/spinner.edc"
#include "widgets/index.edc"
#include "widgets/gengrid.edc"
#include "widgets/photocam.edc"
#include "widgets/map.edc"
#include "widgets/panes.edc"
#include "widgets/panel.edc"
#include "widgets/conformant.edc"
#include "widgets/calendar.edc"
#include "widgets/colorselector.edc"
#include "widgets/flipselector.edc"
#include "widgets/diskselector.edc"
#include "widgets/fileselector.edc"
#include "widgets/layout.edc"
#include "widgets/segment_control.edc"
#include "widgets/player.edc"
#include "widgets/video.edc"
#include "widgets/naviframe.edc"
#include "widgets/multibuttonentry.edc"
#include "widgets/popup.edc"
#include "ews.edc"
#include "widgets/pointer.edc"
}


위에서 사용한 #include 문은 현재 파일이 있는 위치를 기준으로 상대경로를 사용했습니다.

widgets 디렉토리 아래에 있는 수많은 edc 파일을 include하였지요.

edc 파일들을 하나씩 열어보면 모두 group인 것을 알 수 있습니다.

collections를 품고 있는 edc는 단 하나도 없습니다.

왜냐하면 이미 collections로 감싸졌기 때문입니다.


위의 예에서는 모든 위젯을 하나의 collections에서 관리하고 있습니다.

- 이처럼, 하나의 프로젝트 단위에서는 하나의 collections를 사용할 수 있습니다.

- 혹은, 각각의 위젯을 하나의 collections 단위로 쪼갤 수도 있겠지요.

관리상 용이한 것을 따르시면 됩니다.

꼭 정해진 것은 없습니다.


오늘은 collections에 대해서 간단하게 살펴보았습니다.

다음 시간에는 group에 대해서 훑어보도록 하겠습니다.


그럼 좋은 하루 보내세요~

끝_

+ Recent posts