TheSpecUIframework:Chapter 05

From 흡혈양파의 번역工房
Revision as of 11:49, 22 August 2017 by Onionmixer (talk | contribs) (TheSpecUIframework 4장 내용완료)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
레이아웃 구성

레이아웃 구성

컨테이너의 크기 조정에 위젯의 동작을 배치하고 설명하는 것은, 중요하지만 복잡한 문제입니다. 이 장에서는 Spec 을 사용해서 레이아웃을 표현하는 다양한 방법을 제시합니다.


레이아웃 정보

창이나 UI 의 재사용에서 위젯을 배치하는 것은 중요한 문제입니다. 위젯이 컨테이너에 배치 되어야할 위치를 결정할 때 많은 요인이 작용하기 때문입니다(컨테이너는 창 또는 다른 위젯일 수 있습니다).


간단한 해결책은 바깥쪽에서 둘러싸는 컨테이너의 절대 좌표에 위젯을 배치하는 것이지만, 컨테이너 크기를 조정한다면 동작은 중단되어 버립니다: 절대 좌표를 사용한다면 위젯은 컨테이너와 함께 커지거나 줄어들지 않기 때문입니다. 하지만 절대 좌표로 위치를 지정해도 창 크기를 조정하지 않으면 문제는 되지 않으며, 픽셀단위로 모든 위젯을 완벽하게 배치할 수 있습니다.


여러 가지로 사용될 수 있기 때문에, 배치할 위젯은 여러가지 방법으로 제공되어야 합니다. Spec 은 레이아웃을 수행하는 다양한 옵션을 제공하며 SpecLayout 클래스의 commands 프로토콜에서 메서드를 확인할 수 있습니다. 지금까지는, 레이아웃에서 순서에따라 행과 열만 사용하며 컨테이너에 하나의 위젯을 추가하는 것을 보았습니다. 또한 이 모든 결과에서 행, 열 또는 단일 위젯은 컨테이너에서 항상 사용 가능한 모든 공간을 차지합니다. 이는 기본적인 동작입니다.


SpecLayout 의 add: 메소드는 하나의 위젯만 추가하도록 허용하기 때문에, 사용자 인터페이스에 둘 이상의 위젯이 필요하면 이 장에서 제시하는 레이아웃 중 하나를 지정해야 합니다. 여기서는 위젯을 배치하기위한 두 가지 광범위한 전략(먼저 행과 열을 지정하는 다양한 옵션과 두 번째로 위젯을 자유롭게 배치 할 수 있도록 다양하게 제공되는 방법)에 대해 깊이 논의합니다.이 방법들은 생성되는 UI 의 종류에 따라 각각은 장점과 단점을 갖고 있으므로 절충안을 만들 수 있을만큼 잘 알고있는 것이 가장 좋습니다.


사용할 수 있는 예제

이러한 레이아웃을 설명하기 위해 우리는 두 개의 버튼, 목록 및 텍스트 필드가있는 예제 클래스를 사용합니다. non-layout 코드는 아래에 있습니다

ComposableModel subclass: #LayoutExample
	instanceVariableNames: 'list button button2 text'
	classVariableNames: ''
	package: 'Spec-BuildUIWithSpec'
LayoutExample >> initializeWidgets
	button := self newButton.
	button2 := self newButton.
	list := self newList.
	text := self newText.
	button label: 'i am a button'.
	button label: 'me too!'.


행 및 열 레이아웃

간단하고 자주 사용되는 레이아웃 종류중에 두 개 이상의 위젯을 갖는 경우라면, 위젯을 행이나 열에 정렬하는 경우입니다. Spec 은 SpecLayout 클래스의 newRow: 및 newColumn: 메시지를 사용해서, 설명한 레이아웃을 쉽게 지정할 수있는 방법을 제공합니다. 이 메시지들은 위젯에 주어진 공간 내에서 고르게 분포 된 행이나 열을 각각 만듭니다.

그림 5-1 행 위젯의 스크린샷

예를 들어, 다음의 코드는 목록의 버튼과 행의 버튼을 배치합니다:

LayoutExample >> oneRow
	^ SpecLayout composed
		newRow: [ :row | row add: #list; add: #button ];
		yourself


위의 코드는 newRow: 메시지를 사용하여 위젯 행을 만드는 레이아웃 메서드입니다. 메시지의 인수는 하나의 인수로 된 블록이며, 블록 인수는 SpecRowLayout 의 인스턴스를 포함합니다. 그런 다음 위젯이 이 레이아웃 객체에 추가되어 행을 모두 정렬합니다.


아래의 코드는 이러한 레이아웃 명세를 가진 예제를 보여주며, 그 결과 UI는 그림 5-1과 같다. title: 메시지는 별도의 설명조차 필요없다.

| le |
 le := LayoutExample new.
 le title: 'RowOfWidgets'.
 le openWithSpec: #oneRow


위젯을 열로 렌더링하면 newRow: 메시지 대신 newColumn 메시지가 사용됩니다. 이 메시지는 인수가 하나 인 블록을 사용하며, 블록 인수는 SpecColumnLayout 의 인스턴스를 포함합니다.

LayoutExample >> oneColumn
	^ SpecLayout composed
		newColumn: [ :col | col add: #list; add: #button ];
		yourself


아래 코드를 실행하면 그림 5-1 의 결과가 됩니다. 간략하게 보여드리지만, 이 장의 나머지 부분에서는 UI 실행하는 코드를 더 이상 포함하지 않을 것입니다.

그림 5-2 열로 배열된 스크린샷


| le |
 le := LayoutExample new.
 le title: 'ColumnOfWidgets'.
 le openWithSpec: #oneColumn.


Spec 은 SpecLayout 대신 SpecRowLayout 및 SpecColumnLayout 을 직접 사용할 수도 있습니다. 앞의 코드는 oneRow 및 oneColumn 레이아웃 메서드를 보다 간결하게 만들어 줍니다.

LayoutExample >> oneRowConcise
	^ SpecRowLayout composed
			add: #list; add: #button;
			yourself.
LayoutExample >> oneColumnConcise
	^ SpecColumnLayout composed
			add: #list; add: #button;
			yourself


행과 열을 결합하기

newRow: 및 newColumn: 메시지를 서로 다른 조합으로 보내서 행과 열을 결합하면, 보다 복잡한 레이아웃을 만들 수 있습니다. 여기서는 가능성을 설명하기 위해 여기에 몇 가지 예를 보여드리겠습니다.


첫 번째 예제는 두 행의 위젯을 가질 수 있는 방법을 보여줍니다. 여기서는 열을 만들고 두 번 newRow: 를 호출합니다. 결과는 그림 5-3과 같습니다.

그림 5-3 두 행에 대한 스크린샷
그림 5-4 다중 중첩 행과 열의 스크린 샷
그림 5-5 다중 중첩 행의 스크린 샷


LayoutExample >> twoRows
	^ SpecColumnLayout composed
			newRow: [ :row | row add: #text ];
			newRow: [ :row | row add: #button; add: #button2 ];
			yourself


Gnome3 notice header.png
여러 행을 가지려면 SpecColumnLayout 에 추가해야하며, 여러 열을 가지려면 SpecRowLayout 에 추가해야 합니다. SpecComposedLayout 에 addRow: 또는 addColumn: 을 여러 번 보내는 것은 마지막 행 resp 만 생성합니다.
Gnome3 notice footer.png


행과 열은 물론 다중 중첩 될 수도 있습니다. 예를 들어 "SpecRowLayout"에 중첩된 열에 중첩된 행에 두 개의 버튼을 추가합니다. 결과 UI 가 그림 5-2에 나와 있습니다.

LayoutExample >> nesting1
	^ SpecRowLayout composed
		newColumn: [ :col | col add: #list];
		newColumn: [ :col |
			col
				add: #text;
				newRow: [ :row |
					row
						add: #button;
						add: #button2]
		];
		yourself


행을 열에 중첩(그 반대도)하는 것 외에도 행을 행으로(또는 열안의 열) 중첩 할 수 있으므로, 그림 5-5 와 같이 위젯 당 사용 된 공간을 균등하게 반으로 줄일 수 있습니다.

LayoutExample >> nesting2
	^ SpecColumnLayout composed
		newRow: [ :row | row add: #list];
		newRow: [ :row |
			row
				add: #text;
				newRow: [ :inRow |
					inRow
						add: #button;
						add: #button2]
		];
		yourself


행 및 열의 크기 설정

기본적으로 행과 열은 사용할 수 있는 모든 공간을 차지하며, 행(및 열)의 공간은 해당 행(또는 열)의 모든 ​​요소에 고르게 분포됩니다. 이 섹션에서는 행과 열의 크기를 변경할 수있는 세 가지 방법을 보여줍니다. 첫 번째는 사용자가 크기를 조정할 수 있게 하며, 마지막 두 가지는 크기를 지정할 수있는 두 가지 다른 방법입니다.


사용자가 크기를 조정할 수 있도록 UI 구분자(splitters) 추가

간단한 크기 조정 옵션은, 사용자가 위젯을 가로(행) 및 세로(열)로 크기를 조정할 수 있도록 허용하는 것입니다. 크기 조정을 허용하는것은 행 또는 열의 위젯에 대한 add: 메시지 사이에 addSplitter 메시지를 추가해서 이루어집니다. 예를 들어 아래 코드는 목록과 버튼 사이의 수평선을 위 아래로 드래그 할 수 있도록 합니다. 그림 5-2 와같은 결과를 확인할 수 있습니다.

LayoutExample >> oneColumnWithSplitter
	^ SpecColumnLayout composed
			add: #list;
			addSplitter;
			add: #button;
			yourself


픽셀의 크기

계획대로 newRow:height: 및 newColumn:width: 메서드를 사용하면, 행 또는 열의 절대 크기를 픽셀단위를 이용해서 명시적으로 지정할 수 있습니다. 예를 들어, 이전에 그림 5-3 에서 볼 수 있듯이 대형 버튼을 사용하여 못생긴 레이아웃을 피할 수 있는 텍스트 필드 위 또는 아래에 버튼 행을 배치하는 경우에 유용합니다.


아래에서 절대 크기 사용의 간단한 두 가지 예를 보여줍니다. 첫 번째는 그림 5-6 의 UI 로 이어지고 두 번째는 그림 5-7 의 UI 로 연결됩니다.

LayoutExample >> rowOf30
	^ SpecLayout composed
		newRow: [ :row | row add: #list; add: #button] height: 30;
		yourself
LayoutExample >> columnOf50
	^ SpecLayout composed
		newColumn: [ :col | col add: #list; add: #button] width: 50;
		yourself


그림 5-6 30 픽셀 높이 열의 스크린샷
그림 5-6 50 픽셀 행의 스크린샷


또는 열 레이아웃과 행 레이아웃의 내부에서, 각각 add:height:, add:width: 를 사용하여 특정 위젯의 높이와 너비를 나타낼 수도 있습니다.


Gnome3 notice header.png

일부 변경 사항(예: 글자 크기)은 이 수치를 무효화 할 수 있기 때문에, 위젯 크기를 픽셀 단위로 하드 코딩하는 것은 좋지 않은 방법입니다. 이런 조건을 보다 쉽게 만들기 위해 ComposableModel 클래스 는 default 프로토콜에서 현실적인 크기를 취급하는 접근자를 제공합니다.

Gnome3 notice footer.png


높이를 계산하는 접근자의 사용 예는 "toolbarHeight" 입니다 (그림 3-3에서 볼 수 있듯이 프로토콜 메서드 브라우저에서 사용함). 이 접근자는 글꼴 크기에 따라 다르며 버튼 행의 크기 변경에도 유용합니다


비율 레이아웃(Proportional layout)

세가지중, 마지막 옵션은 행 또는 열이 차지해야하는 컨테이너(예 : 창)의 백분율을 지정하는 것입니다. 이 방법은 newRow:top:bottom: 및 newColumn:left:right: 메시지를 사용합니다. 크기를 픽셀 단위로 지정하는 것과 달리, 이러한 메시지를 사용하면 컨테이너 크기를 조정할 때 행 또는 열의 크기가 그에 따라 변경됩니다.


앞의 메시지는 두 개의 숫자를 추가 인수로 사용합니다. 인수는 0 에서 1 사이의 값이어야 합니다. 이 인수들은 요소가 시작되어야 하는 다른쪽 가장자리까지의 거리를 나타내는 백분율입니다. 예를 들어, 창의 왼쪽 끝에서 시작하여 너비의 30 %를 차지하는 열은 너비의 30 %가 오른쪽 가장자리에서 70 % 떨어져 있기 때문에 newColumn: [:c| ...] left: 0 right: 0.7 이 됩니다.


Gnome3 notice header.png
이 두 숫자는 컨테이너의 '반대쪽'을 향한 백분율을 나타내며, 위에서 아래로 또는 왼쪽에서 오른쪽으로의 백분율은 아닙니다.
Gnome3 notice footer.png


아래의 코드는 보다 복잡한 예제입니다. 그림 5-5 의 인위적 변화는 버튼을 비례해서 작게 만듭니다(예를 들어 버튼의 행 높이가 픽셀 단위로 크기를 사용하는 것이 더 적합하기 때문에 이 예제는 인위적입니다). 첫 번째 행은 공간의 처음 80% (since top: 0 bottom: 0.2)부터 두 번째 행은 마지막 20 % (since top: 0.8 bottom: 0)부터 사용합니다. 또한 하단 행에서 텍스트 필드는 첫 번째 55% 의 공간(==left: 0 right: 0.45==)을 차지하고 두 개의 버튼은 마지막 45% (==left: 0.55 right:0==). 이러한 코드의 결과는 그림 5-8 에서 볼 수 있습니다.

nestingTB
	^ SpecColumnLayout composed
		newRow: [ :row | row add: #list] top: 0 bottom: 0.2 ;
		newRow: [ :row |
			row
				newColumn: [:c | c add: #text] left: 0 right: 0.45;
				newColumn: [ :c |
					c newRow: [ :inRow |
						inRow
							add: #button;
							add: #button2]] left: 0.55 right: 0
		] top: 0.8 bottom: 0 ;
		yourself


그림 5-2 비례 행 및 열 사용의 스크린 샷


행 또는 열이 없는 레이아웃

물론 행과 열이 사용자 인터페이스를 배치 할 수있는 유일한 방법은 아닙니다. Spec 을 사용하면 위젯을 보다 자유롭게 배치 할 수 있습니다. 이 방법은 둘러싸는 컨테이너의 절대 위치를 사용하거나, 윈도우 크기 조정을 고려한 상대 위치 지정을 사용해서 수행 할 수 있습니다. 다양하게 표시되는 위치 옵션을 사용할 수있는 방법을 지금부터 보여 드리겠습니다.


절대 위젯 위치

위젯을 배치 할 수 있는 첫 번째 방법은 컨테이너를 절대 위치(absolute positions)로 지정하는 것입니다. 절대 위치를 지정하려면 SpecLayout 의 메서드인 add:top:bottom:left:right: 가 사용됩니다. 이 메서드는 추가로 4 개의 인수를 필요로 하는데, 각각의 값은 거리를 픽셀 수로 나타낸다. 픽셀 값은 양수(positive)가 되어야 합니다. 지정된 가장자리에서 반대편 가장자리까지의 거리를 나타냅니다.


예를 들어 아래에서 위쪽에서 10 픽셀, 아래에서 200 픽셀, 왼쪽에서 10 픽셀, 오른쪽에서 10 픽셀의 값을 가지는 버튼 레이아웃을 수행합니다. 그 결과는 그림 5-9와 같습니다

LayoutExample >> oneButtonAbsolute
	^ SpecLayout composed
		add: #button top: 10 bottom: 200 left: 10 right: 10;
		yourself


Gnome3 notice header.png
인자의 기반 논리는 5.4 절의 newRow:top:bottom: 및 newColumn:left:right 에서와 동일합니다. 각각의 거리(distance)는 컨테이너의 '다른 끝'을 향하고 있습니다.
Gnome3 notice footer.png


그림 5-9 절대 배치 된 버튼의 스크린 샷

+Screen shot of an absolutely placed button>file://figures/AbsoluteButton.png|width=50|label=fig_AbsoluteButton+


Gnome3 notice header.png
창 크기를 조정하면 절대 위젯 위치 사용은 완전히 중단됩니다. 따라서 크기를 조정할 수없는 창과 조합해서 만 사용하는 것이 가장 좋습니다.
Gnome3 notice footer.png

}


상대 위젯 위치

상대 위젯 위치는 컨테이너의 크기가 조정되는 방식에 따라 위젯의 크기를 조정합니다. SpecLayout 의 add:origin:corner: 메소드는 원점에서 꼭짓점까지의 백분율 단위로 위젯의 상대적 레이아웃을 지정합니다. 이 두 점은 각각 위젯의 왼쪽 위 모서리와 오른쪽 하단 모퉁이를 나타냅니다. 인수는 컨테이너의 비율을 나타 내기 때문에 0@0 와 1@1 사이의 값 이어야 합니다.


예를 들어 아래에 컨테이너 크기의 절반 인 버튼 하나를 컨테이너 중앙에 배치합니다 (그림 5-10 참조).

LayoutExample >> oneButtonSmaller
	^ SpecLayout composed
		add: #button origin: (0.25 @ 0.25) corner: (0.75 @ 0.75);
		yourself
그림 5-2 항상 가운데에 버튼이 위치하는 스크린샷


또는 add:top:bottom:left:right: 메서드는 백분율 인수 (예 : 0 에서 1 사이)와 함께 사용하여 반대쪽 가장자리에서 계산 된 백분율로 상대 레이아웃을 생성 할 수 있습니다. 예를 들어, 아래의 코드는 위의 코드와 정확히 동일한 결과를 생성합니다. 하지만 두 가지 이유 때문에 add:top:bottom:left:right: 메서드의 사용은 권장하지 않습니다. 왜냐하면 첫번째로 상대(값)와 절대(값) 사이에는 혼란이 있을 수 있습니다. 백분율 또는 픽셀 수로 0 또는 1 값을 해석해야 하는가에 대한 문제입니다. 두번째로 다음에 논의할 오프셋의 사용을 지원하지 않기 때문입니다.

LayoutExample >> oneButtonSmallerAlternative
	^ SpecLayout composed
		add: #button top: 0.25 bottom: 0.25 left: 0.25 right: 0.25;
		yourself


오프셋과 상대적 처리

위젯의 상대 위치 상태에서는 컨테이너가 커지고 축소 될 때 위젯 크기를 조정할 수 있지만, 위젯은 항상 컨테이너 경계 바로 옆이나 옆에 배치된다는 단점이 있습니다. 예를 들어, 컨테이너 경계의 5% 에 ​​위젯을 배치하면, 특정 창의 크기에 대해 올바른 두께의 경계선을 만들 수 있지만, 크기가 조정될 때에는 너무 크거나 작을 수도 있습니다. 정말로 필요한 것은 위젯을 상대적으로 배치 할 수 있는 방법이지만, 절대 오프셋을 지정해서 위젯간의 간격을 동일하게 만들수도 있습니다. 이런 복합적인 방법은 SpecLayout 의 add:origin:corner:offsetOrigin:offsetCorner: 메소드를 사용하면 가능합니다. origin:corner: 의 인수는 add:origin:corner: 메서드와 동일하며, 두 오프셋 인수 모두다 인수로 간주됩니다. 이러한 처리방식은, 원점이 왼쪽 상단 모서리에 있는 클래식 컴퓨터 그래픽 좌표계에서 해당 모서리의 픽셀 수를 나타냅니다. 결과적으로 "x"또는 "y"구성 요소는 음수(negative)가 될 수도 있습니다.


예를 들어, 아래의 코드는 서로 상단 부분에 두 개의 버튼을 배치합니다. 각각의 버튼은, 창 공간의 절반에서 10 픽셀의 창 테두리를 빼고 10 픽셀 사이의 공간을 차지합니다.

LayoutExample >> twoButtonsRelativeOffset
	^ SpecLayout composed
		add: #button origin: (0 @ 0) corner: (1 @ 0.5)
				 offsetOrigin: (10 @ 10) offsetCorner: (-10 @ -5);
		add: #button2 origin: (0 @ 0.5 ) corner: (1 @ 1)
				 offsetOrigin: (10 @ 5) offsetCorner: (-10 @ -10);
		yourself


행과 열의 상대적 위젯 위치

마지막으로, 다른 위젯을 사용해서 행과 열을 쉽게 구성 할 수 있게 하기위해, 행과 열을 상대적으로 상대적으로 오프셋으로 배치 할 수 있습니다. newRow: 및 newColumn: 메서드에는 origin:corner: 및 origin:corner:offsetOrigin:offsetCorner: 접미사(suffix)가 있는 다른 형태도 있습니다. 이 메서드들은 상대 레이아웃 옵션에 행과 열의 입장에서 바라본 위젯을 추가합니다.


이러한 위치 사용의 한 예는 고객 만족 사례의 첫 번째 장에 있습니다. 이 예제의 레이아웃 코드는 아래에 있으며 그 결과는 그림 2-1에서 볼 수 있습니다.

CustomerSatisfaction class >> defaultSpec
	^ SpecLayout composed
		newRow: [ :row |
				row add: #buttonHappy; add: #buttonNeutral; add: #buttonBad ]
			origin: 0 @ 0 corner: 1 @ 0.7;
		newRow: [ :row |
				row add: #screen ]
			origin: 0 @ 0.7 corner: 1 @ 1;
		yourself


결론

이 장에서는 Spec 이 제공하는 다양한 레이아웃 전략에 대해 논의했습니다. 사용자 인터페이스에 둘 이상의 위젯이 있는 경우에는, 이러한 전략 중 하나를 사용해야합니다. 지금까지 레이아웃의 두 가지 전략에 대해 이야기했습니다. 첫째로 행과 열 기반의 레이아웃과 두 번째로 절대 및 상대 레이아웃입니다(그리고 그 둘을 섞은 형태도 있습니다). UI 를 구성 할 때 각 레이아웃의 장점 및 단점을 파악하는 것이 현명하므로. 사용될 레이아웃의 선택은 사용자 인터페이스의 시각적 모양을 고려한 좋은 균형 관계에 해당합니다.


Notes