CafeM0ca

[JUCE]튜토리얼5 Customise the look and feel of your app 본문

Programming/JUCE

[JUCE]튜토리얼5 Customise the look and feel of your app

M0ca 2018. 2. 15. 01:55
반응형

//발번역 https://docs.juce.com/master/tutorial_look_and_feel_customisation.html

tutorial_look_and_feel_customisation.zip


어플리케이션에서 자신만의 스킨을 적용해서 커스텀마이징해보자.


데모 프로젝트를 실행해보면 아래와 같다.


LookAndFeel 클래스는 JUCE에서 사용자 정의된 GUI를 만드는 기본 도구다. LookAndFeel 클래스를 사용하여 현재 컴포넌트가 갖고있는 기본 색상을 간단하게 바꿀 수 있다. 그뿐만아니라 많은 컴포넌트들을 커스텀마이징하여 그릴 수 있다. 


colours 커스텀하기

컴포넌트에 LookAndFeel 객체가 적용되면 해당 컴포넌트에 적용되고 구체적으로 look-and-feel이 다른경우가 아니면 자식 컴포넌트도 적용된다. 

look-and-feel 시스템으로 기본 JUCE 컴포넌트들의 요소들을 colours로 오버라이딩 할 수 있다. 예를들어 MainContentComponent 생성자에 다음 코드를 추가하면 다이얼들이 빨간색으로 바뀐다.


getLookAndFeel().setColour (Slider::thumbColourId, Colours::red);



두개의 다이얼을(저 동글동글한 손잡이) 다르게 설정하려면 새로운 LookAndFeel 인스턴스를 생성하고 각각의 다이얼에 적용하면 된다.

LookAndFeel_V4 객체를 맴버로 추가하자.(JUCE의 look-and-feel 기본 도구 클래스다)

private:
LookAndFeel_V4 otherLookAndFeel; // [1]
Slider dial1;
Slider dial2;
TextButton button1;
TextButton button2;

아까 썼던 LookAndFeel.setColour...에서 otherLookAndFeel로 바꿔주면 된다.

생성자 맨 끝에 추가해준다.

dial1.setLookAndFeel (&otherLookAndFeel);



물론 이 예제는 슬라이더 객체에 Slider::thumbColourld 색상을 직접 설정하는것보다 이점이 없다. 그러나 앱에서 여러개의 슬라이더들을 다른 목적으로 사용할 수있는데 슬라이더들을 한가지 원하는 목적으로 사용하기위해 컬러들을 하나씩 설정할 수 있고 슬라이더들을 다른 목적으로 색을 설정할 수 있다. (But your app may use multiple sliders for different purposes where you want sliders for one purpose to use one set of colours and sliders for other purposes to use different sets of colours.)

이 방법을 사용하면 각 슬라이더에 해당 유형의 적절한 look-and-feel이 지정되어있는 한 색상을 전체적으로 변경할 수 있다.

이 방법은 커스텀마이징하기위해 실제 코드를 작성할때 확실해진다. look-and-feel 클래스를 커스텀해보자.



look-and-feel 커스텀하기

어떤 컴포넌트를 커스텀하기위해 우리는 LookAndFeel 클래스로부터 상속된 새 클래스를 만들어야한다. 만약 LookAndFeel 클래스로부터 직접적으로 상속했으면 순수한 가상함수 구현이 필요하다. 이미 정의된 모든 함수가 있는 클래스 중 하나에서 상속하는 것이 훨씬 더 실용적이다. 그러고나서 필요한것만 오버라이딩하면된다. 간단한 커스텀마이징한 look-and-feel을 만들어보자.

생성자에 추가했던 다음 코드를 지우자.

이제 LookAndFeel_v4 클래스를 상속한 새 클래스를 MainContentComponent 클래스 전에 추가하자. (MainComponent.h)

class OtherLookAndFeel : public LookAndFeel_V4
{
public:
OtherLookAndFeel()
{
}
};

이 코드를 실행하기 전에 otherLookAndFeel 클래스의 맴버를 OtherLookAndFeel로 바꾸자

private:
OtherLookAndFeel otherLookAndFeel; // 이것말이여~
Slider dial1;
Slider dial2;
TextButton button1;
TextButton button2;

결과는 아까와 같다.



커스텀 그리기

LookAndFeel 클래스는 많은 다른 타입의 컴포넌트를 위한 많은 함수가 있다. 이 함수들은 특정 컴포넌트들이 LookAndFeelMethods라는 중첩 클래스내에서 관련 컴포넌트들을 쉽게 찾을 수 있도록 정의되있게 디자인되어있다.


슬라이더 커스텀

예를들어 JUCE API문서와함께 Slider::LookAndFeelMethods를 보자. 목록중에서 Slider::LookAndFeelMethods::drawRotarySlider() 함수를 볼 수 있다. 이것을 OtherLookAndFeel 클래스에 오버라이딩하자. 클래스 선언부분에 다음을 추가하자.

void drawRotarySlider (Graphics& g, int x, int y, int width, int height, float sliderPos,
const float rotaryStartAngle, const float rotaryEndAngle, Slider& slider) override
{
//...
}

  • g: The Graphics context.
  • x: The x coordinate of the top-left of the rectangle within which we should draw our rotary slider.
  • y: The y coordinate of the top-left of the rectangle within which we should draw our rotary slider.
  • width: The width of the rectangle within which we should draw our rotary slider.
  • height: The height of the rectangle within which we should draw our rotary slider.
  • sliderPos: The position of the slider as a proportion in the range 0..1 (this is independent of the slider's actual range of values).
  • rotaryStartAngle: The start angle of the dial rotation (in radians).
  • rotaryEndAngle: The end angle of the dial rotation (in radians).
  • slider: The Slider object itself.

g: The Graphics context

x: 회전식 슬라이더를 그리는 직사각형의 왼쪽 상단의 x좌표

y: ;; 왼쪽 상단의 y좌표

width: 너비

height: 높이

sliderPos: 0..1 범위의 비율로 슬라이더의 위치(실제 범위값과 무관)

rotaryStartAngel: 시작 각도(라디안)

rotaryEndAngle: 끝 각도(라디안)

slider : Slider 객체


Note: x,y,너비,높이 인수는 슬라이더가 사용할 수 있는 텍스트 상자의 크기와 위치를 고려한다. 따라서 슬라이더의 위치와 크기에 접근하여 그 값을 사용할 수 있다.


이제 함수 몸체를 적어보자. 간단하게 다이얼을 그리고 다이얼이 가리키는 부분을 선으로 표시하는 가득 채운 원을 그리자. 전달한 값을 기반으로 한 계산을 돕기 위해 임시 변수가 필요하다.

const float radius = jmin (width / 2, height / 2) - 4.0f;
const float centreX = x + width * 0.5f;
const float centreY = y + height * 0.5f;
const float rx = centreX - radius;
const float ry = centreY - radius;
const float rw = radius * 2.0f;
const float angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);

Note: angle변수가 다이얼이 가리키는 좌표를 갖고있다.


아래 코드는 다이얼을 색칠하고 그린다.

// fill
g.fillEllipse (rx, ry, rw, rw);
// outline
g.drawEllipse (rx, ry, rw, rw, 1.0f);

포인터를 그리기 위해 첫번째로 Path 객체를 사용해야하는데 우리가 필요로하는 좌표로 이동하고 회전시킬것이다.

Path p;
const float pointerLength = radius * 0.33f;
const float pointerThickness = 2.0f;
p.addRectangle (-pointerThickness * 0.5f, -radius, pointerThickness, pointerLength);
p.applyTransform (AffineTransform::rotation (angle).translated (centreX, centreY));

그러고 나서 이 경로를 채워 포인터를 그린다.

// pointer
g.fillPath (p);

Note: 이 섹션에서 완성된 코드는 MainComponent_02.h에 있다.

연습하기: 포인터가 그리는걸 변조하시오. 다른 길이, 약간의 두꺼운 둥근 사각형을 시도하거나 화살표를 그릴 수 있다.


look-and-feel 메소드를 통해 Slider중 하나를 간단하게 커스텀할 수 있는 것을 보여줬다. 하지만 다른 메소드에 이 원리를 적용할 수 있다.

혹시 다르게 커스텀을 만들기 위해 궁리하고 있으면 가장 좋은 방법은LookAndFeel_V4나 LookAndFeel_V2클래스를 기반으로하여 만드는 것이다.


Note: LookandFeel_V4 클래스는 LookAndFeel_V2 클래스로부터 몇명 메소드들을 상속하고 재정의되었다.



버튼 커스텀

버튼을 커스텀해보자. MainComtentComponent에  다음을 추가하자.

setLookAndFeel (&otherLookAndFeel);

이것은 당연히 우리의 다이얼이 우리가 이전 섹션에서 커스텀한 것을 나타낸다. 이제 Button::LookAndFeelMethods::drawButtonBackground() 함수의 정의를 보자.

void drawButtonBackground (Graphics& g, Button& button, const Colour& backgroundColour,
bool isMouseOverButton, bool isButtonDown) override
{
//...
}

g: the Graphics context // 이거 뭐라고 해석해야할지 진짜 애메하다.  

button: 버튼 객체

backgroundColour: 백그라운드에 기본이 되는 색

isMouseOverButton: 마우스 포인터가 버튼의 범위 안에 있는지 여부

isButtonDown: 마우스 버튼의 작동 중지 여부


함수 몸체에 다음을 추가하자.

Rectangle<int> buttonArea = button.getLocalBounds();
g.setColour (backgroundColour);
g.fillRect (buttonArea);

컴파일하고 실행하면 아래와 같다.


이것과 상호 작용하면 버튼이 마우스 포인터 상호 작용에 시각적으로 반응하지 않음을 알 수있다. (버튼 클릭하면 바뀌는게 없다는 의미)

drawButtownBackground() 함수에 다음을 추가하자.

Rectangle<int> buttonArea = button.getLocalBounds();
const int edge = 4;
buttonArea.removeFromLeft (edge);
buttonArea.removeFromTop (edge);
// shadow
g.setColour (Colours::darkgrey.withAlpha (0.5f));
g.fillRect (buttonArea);
const int offset = isButtonDown ? -edge / 2 : -edge;
buttonArea.translate (offset, offset);
g.setColour (backgroundColour);
g.fillRect (buttonArea);

버튼이 이제 클릭에따라 움직임이 나타날 것이다. 그러나 텍스트는 여전히 고정이다. 그래서 Button:LookAndFeelMethods::drawButtonBackground() 함수를 신뢰성있게 오버라이딩 해야한다. 이 함수를 작성하기위해 LookAndFeel_V2클래스에서 복사하고 OtherLookAndFeel 클래스에 작성하자.

void drawButtonText (Graphics& g, TextButton& button, bool isMouseOverButton, bool isButtonDown) override
{
Font font (getTextButtonFont (button, button.getHeight()));
g.setFont (font);
.withMultipliedAlpha (button.isEnabled() ? 1.0f : 0.5f));
const int yIndent = jmin (4, button.proportionOfHeight (0.3f));
const int cornerSize = jmin (button.getHeight(), button.getWidth()) / 2;
const int fontHeight = roundToInt (font.getHeight() * 0.6f);
const int leftIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnLeft() ? 4 : 2));
const int rightIndent = jmin (fontHeight, 2 + cornerSize / (button.isConnectedOnRight() ? 4 : 2));
const int textWidth = button.getWidth() - leftIndent - rightIndent;
if (textWidth > 0)
leftIndent, yIndent, textWidth, button.getHeight() - yIndent * 2,
}

텍스트가 그려지는 기준점을 drawButtonBackground() 함수에서 수정해야한다. 

//...
const int textWidth = button.getWidth() - leftIndent - rightIndent;
const int edge = 4;
const int offset = isButtonDown ? edge / 2 : 0;
if (textWidth > 0)
leftIndent + offset, yIndent + offset, textWidth, button.getHeight() - yIndent * 2 - edge,
}

컴파일하고 실행해보면 버튼 눌리면 그림자 효과가 생길것이다.


완성된 코드는 MainComponent_03.h에서 찾아볼 수 있다.


연습하기: 버튼이 마우스포인터에 응답하도록 수정해보시오. 예를들면 배경색을 조정해보거나 그림자의 색을 바꿔보거나 직사각형의 크기나 위치를 바꿔보거나


요번 튜토리얼에서는 look-and-feel의 기본 색을 커스텀,새롭게 제작, 버튼과 슬라이더를 커스텀마이징 해보았다.

반응형
Comments