CafeM0ca

[JUCE]튜토리얼4 Component class,parents,children 본문

Programming/JUCE

[JUCE]튜토리얼4 Component class,parents,children

M0ca 2018. 2. 12. 15:46
반응형

//발번역 https://juce.com/doc/tutorial_component_parents_children


tutorial_component_parents_children.zip


이번 튜토리얼에서는 Component 클래스의 계층적 특징을 설명하는것에 의해 하나의 컴포넌트가 하나 이상의 중첩된 자식 컴포넌트를 가질 수 있다.


압축 풀고 실행시키면 아래와 같다.

 


모카는 컴파일 에러가 뜬다. 자료형이 일치하지 않다고 하는데 getLocalBounds() 함수가 Rectangle<int>인데 어쩌냐.. 왜 이렇게 만든건지도 모르겠다.




컴포넌트 클래스의 계층

대부분의 유저 인터페이스는 몇개의 요소들로 이루는데 텍스트,버튼,슬라이더,메뉴같은 것들이다. 예를 들어 다음 스크린샷은 AudioDeviceSelectorComponent 클래스를 보여준다. 버튼과 몇개의 레이블과 메뉴(연속되는 박스들), 라디오 버튼,오디오 레벨 지시자가 있다.



일부 개별 사용자 인터페이스 요소는 다른 사용자 인터페이스와 요소를 그룹화하여 유용하게 제어할 수 있다. 예를 들면 JUCE Slider 클래스는 슬라이더 자체 뿐만아니라 텍스트 박스(현재 슬라이더의 값)도 보여줄 수 있다.


이 클래스들 각각에서 개별 요소들을 계층 구조의 분리된 부분으로 분리함으로써 훨씬 인터페이스 레이아웃 디자인이 쉽다.일부 컴포넌트들은 pain() 함수를 써서 그릴것이다. 다른 컴포넌트들은 쉽게 또 다른 컴포넌트들을 가질 수 있다. 일부 컴포넌트는 다른 컴포넌트를 포함하고 그리기를 수행할 수 있다. 디자인 선택은 매우 유연하다.


MainContentComponent 클래스

MainContentComponent 클래스는 또 다른 컴포넌트 클래스의 인스턴스가 맴버를 포함하고 있다. SceneComponent 클래스는 실제 장면을 그린다.



private:
SceneComponent scene;
//==============================================================================
};
#include "SceneComponent.h" //MainComponent.h에 추가

 MainContentComponent 생성자 내에서 SceneComponent 객체는 자식 컴포넌트로 추가되고 MainContentComponent 객체는 부모가된다.


주의: 자식 컴포넌트는 반드시 하나 이상의 부모 컴포넌트가 언제든 있어야한다. 부모 컴포넌트로부터 자식 컴포넌트를 지울수 있고 지워진 자식 컴포넌트는 다른 부모 컴포넌트에 추가할 수 있다.


자식 컴포넌트가 표시되기 위해서는 눈에 보이게 할 필요가 있다. 여기 두 단계는 별도로 수행할수 있으나 JUCE에서는 두 작업을 한번에 Component::addAndMakeVisible() 함수를 사용해 수행하는것이 일반적이다.

MainContentComponent()
{
addAndMakeVisible (scene);
setSize (600, 400);
}

자식 컴포넌트 범위 설정

MainContentComponent 클래스는 생성하는 동안 크기를 알아서 설정하지만, 많은 컴포넌트 객체들은 0으로 초기화된다. Component::setSize() 함수는  MainContentComponent::resized() 함수를 차례대로 호출할 것이다. 여기 자식 컴포넌트의 사이즈,위치를 설정하기 좋은곳이 있다.

void resized()
{
scene.setBounds (0, 0, getWidth(), getHeight());
}

중요한것은 ScenceComponent::setBounds() 함수안의 좌표가 부모 컴포넌트랑 연관이 있다. 이 의미는 왼쪽 상단 코너의 부모 컴포넌트가 (0,0)지점이면 자식 컴포넌트도 이 좌표랑 관련있게 자리잡게된다.  사실 SceneComponent 객체는 MainContentComponent 객체의 전체를 체운다. 이것을 쓰는 또다른 방법은  Component::getLocalBounds() 함수를 사용하는 것이다. Component::getLocalBounds() 함수는 호출되는 Rectangle 객체가 보여주는 컴포넌트의 범위를 반환한다. 직사각형 (0,0)의 좌표와 넓이,높이가 결과가 된다. Rectangle 객체는 SceneComponent::setBounds() 함수로 대체될 수 있다. 


void resized()
{
scene.setBounds (getLocalBounds());
}


자식 컴포넌트는 부모 컴포넌트의 범위를 초과하도록 배치될 수 있지만 부모 컴포넌트 요소의 범위를 벗어나는것은 그려지지 않는다. 만약 컴포넌트를 볼 수 없다면 범위가 제대로 설정되었는지 확인해봐야한다. (ex: 부모 컴포넌트의 resize() 함수)



 The scene

Scene 클래스는 자신을 그리고 두개의 자식 컴포넌트를 가지고 있다.(바닥,집) SceneComponent.h를 보자


#include "FloorComponent.h"
#include "HouseComponent.h"
class SceneComponent : public Component
{
// ...
private:
FloorComponent floor;
HouseComponent house;
//==============================================================================
};


FloorComponent와 HouseComponent 객체가 추가되어 생성자에 표시된다.

SceneComponent()
{
addAndMakeVisible (floor);
addAndMakeVisible (house);
}

하늘을 그리기 위해, 컴포넌트를 완전히 채운다. SceneComponent::paint() 함수에 light blue를 추가하자.

바닥과 집의 면적과 좌표를 SceneComponent::resize() 함수에 있다.

void resized()
{
floor.setBounds (10, 297, 580, 5);
house.setBounds (300, 70, 200, 220);
}

컴포넌트는 자기자신을 먼저 그리고나서 자식 컴포넌트는 그 위에 그려진다. Component::paintOverChildren() 함수를 자식 컴포넌트 위에 그리고 싶다면 오버로딩할 수 있다. 자식 컴포넌트는 부모 컴포넌트에 추가된 순서대로 그려진다. 이것은 Component::toFront(),Component::toBack(),Component::toBehide(),Component::setalwaysOnTop()함수에 의해 조절 될 수 있다.



바닥

{
g.drawLine (0, getHeight() / 2, getWidth(), getHeight() / 2, 5);
}

HouseComponent자체는 paint함수가 없지만 두개의 컴포넌트가 paint함수가 있으니 상관없다.

#include "WallComponent.h"
#include "RoofComponent.h"
class HouseComponent : public Component
{
// ...
private:
WallComponent wall;
RoofComponent roof;
//==============================================================================
};


WallComponent와 RoofComponent는 HouseComponent의 생성자에서 만들어진다.

HouseComponent()
{
addAndMakeVisible (wall);
addAndMakeVisible (roof);
}

HouseComponent::resize() 함수에서 상대적으로 배치된다.

    void resized()
    {
    const int separation = jlimit (2, 10, getHeight() / 20); // [1] 지붕과 벽사이를 계산하여 나눈다. 이 높이를 1/20로 만들고 jlimit() 함수로 2픽셀보다 작게만든다. 높이가 작은 경우에도 항상 지붕과 벽 사이에 틈새가 생긴다.
    roof.setBounds (0, 0, getWidth(), getHeight() * 0.2 - separation / 2);
    wall.setBounds (0, getHeight() * 0.20 + separation / 2, getWidth(), getHeight() * 0.80 - separation);
    }

    벽은 체크보드 패턴으로 가득 체운다. (안돌아가는게 문제지만..)

    void paint (Graphics& g)
    {
    }


    RoofComponent 클래스는 Path 객체를 사용해서 삼각형을 그린다.

    void paint (Graphics& g)
    {
    Path roof;
    roof.addTriangle (0, getHeight(), getWidth(), getHeight(), getWidth() / 2, 0);
    g.fillPath (roof);
    }

    태양을 추가해봅시다.

    SunComponent.h에 다음을 추가하자.


      #include "FloorComponent.h"
      #include "HouseComponent.h"
      #include "SunComponent.h"
      class SceneComponent : public Component
      {
      // ...
      private:
      FloorComponent floor;
      HouseComponent house;
      SunComponent sun;
      //==============================================================================
      };

      태양을 추가했으니 ScnenComponent 생성자에도 추가하자.

      SceneComponent()
      {
      addAndMakeVisible (floor);
      addAndMakeVisible (house);
      addAndMakeVisible (sun);
      }

      좌표는 오른쪽 상단으로

      void resized()
      {
      floor.setBounds (10, 297, 580, 5);
      house.setBounds (300, 70, 200, 220);
      sun.setBounds (530, 10, 60, 60); // [7]
      }

      다 설정했으니 그려줍시다.(SunCompoment.h)

      void paint (Graphics& g)
      {
      const float lineThickness = 3.0f;
      g.drawEllipse (lineThickness * 0.5f,
      lineThickness * 0.5f,
      getWidth() - lineThickness,
      getHeight() - lineThickness,
      lineThickness);
      }

      좌표찍을때 조심해야할 부분이 컴포넌트 안에 배치해야한다. 딱맞게 찍으면 찍은 부분에서 두께의 절반이 좌표밖나간다.


      스크린샷보면 원이 짤려있죠?


      저런거 계산하기 싫으면 Component::setPaintinglsUnclipped() 함수 사용해서 컴포넌트 경계밖으로 넘어서는걸 허용할 수 있다.


      컴포넌트 재활용하기


      완성된 HouseComponent를 재활용해봅시다.

      class SceneComponent : public Component
      {
      private:
      FloorComponent floor;
      HouseComponent house;
      HouseComponent smallHouse; // 요기요기
      SunComponent sun;
      //==============================================================================
      };
      SceneComponent()
      {
      addAndMakeVisible (floor);
      addAndMakeVisible (house);
      addAndMakeVisible (smallHouse); // 생성자에 객체 추가해주시고~
      addAndMakeVisible (sun);
      }
      void resized()
      {
      floor.setBounds (10, 297, 580, 5);
      house.setBounds (300, 70, 200, 220);
      smallHouse.setBounds (50, 50, 50, 50); // 좌표도 찍어줍시다.
      sun.setBounds (530, 10, 60, 60);
      }

      그러면 하나가 뚝딱..


      이 재활용한거는 SceneComponent_03.h에 있다.


      요번에 배운거: 번에는 세분화하여 컴포넌트를 구성하고 배치하는법과 재활용 하는법을 배웠다.

      반응형
      Comments