TheSpecUIframework:Chapter 06
- 창 관리하기
창 관리하기
이 책에서는 지금까지 ComposableModel 의 재사용에 대해 이야기하고, Spec의 기본적인 기능에 대해 논의했으며, 사용자 인터페이스의 위젯을 배치하는 방법을 제시했습니다. 아직 빠진 부분이 있는데, 작업중인 사용자 인터페이스에서 창 내부에 위젯을 모두 표시하는 것입니다. 지금까지의 예제에서는 창 관리를 위한 Spec의 몇 가지 기능은 살펴봤지만, 기본적으로 창을 여는 것을 제한했습니다.
이 장에서는 Spec을 사용하여 창을 관리하는 방법에 대한보다 완전한 개요를 제공합니다. 창의 열기와 닫기, 내장된 다이얼로그 박스 기능, 창 크기 조정 및 모든 종류의 창 장식을 보여줍니다.
실용 예제
실용 예제
사용이 가능한 창 환경설정 옵션을 설명하기 위해서, 두 개의 버튼이 나란히 배치 되어있는 간단한 "WindowExample"클래스를 사용합니다. 이 버튼들은 아직 어떤 행동과도 관련이 없으며, 필요한 내용은 이 장의 아래에있는 예제에서 추가될 것입니다.
ComposableModel subclass: #WindowExample
instanceVariableNames: 'button1 button2'
classVariableNames: ''
package: 'Spec-BuildUIWithSpec'
WindowExample >> initializeWidgets
button1 := self newButton.
button2 := self newButton.
button1 label: '+'. button2 label: '-'.
WindowExample class >>defaultSpec
^ SpecRowLayout composed
add: #button1; add: #button2; yourself.
창 또는 대화 상자 열기
사용자 인터페이스는 일반적(normal)인 창으로 열리거나 대화 상자로 열 수 있습니다. 즉 장식 과 Ok 및 Cancel 버튼이 포함되어 있다는 얘기입니다. 여기서는 대화 상자와 관련된 구성 옵션을 포함해서, 이 작업이 어떻게 수행되는지 보여줍니다.(창 장식에 대한 자세한 내용은 6.3 절을 참조하십시오)
창 열기
이전 섹션에서 살펴본 것처럼 사용자 인터페이스를 열려면, 해당 인터페이스의 ComposableModel 을 인스턴스화하고 openWithSpec 메시지를 보내야합니다. 이렇게 하면 사용자 인터페이스가 포함 된 WindowModel 의 인스턴스가 만들어지며 화면의 창에 표시됩니다.
우리는 이미 openWithSpec: 메서드, 특히 5 장에서 레이아웃 메시지의 이름을 인수로 사용하는 방법을 보았습니다.
기본 레이아웃을 사용하는 대신에(이 방법은 섹션 4.3 에 적혀 있습니다), 열려있는 UI 는 해당 메소드가 반환 한 레이아웃을 사용합니다.두 번째 변형은 SpecLayout 인스턴스(또는 그 하위 클래스의 인스턴스)를 사용하는 openWithSpecLayout: 입니다.
예를 들어 아래에서는 WindowExample 에 대해 창을 여는 세 가지 방법을 보여줍니다. 3 개의 동일한 창을 엽니다.
| we |
we := WindowExample new.
we openWithSpec.
we openWithSpec: #defaultSpec.
we openWithSpecLayout: we defaultSpec.
대화 상자 열기 및 설정 옵션
Spec 은 UI 를 Ok 및 Cancel 버튼을 사용하는 간단한 대화 상자를 여는 쉬운 방법을 제공합니다. 이렇게 하려면 다음과 같이 openDialogWithSpec 메시지를 보내십시오.
| we diag |
we := WindowExample new.
diag := we openDialogWithSpec.
이 결과(위 예제서는 diag 변수에 할당됨)는 DialogWindowModel 클래스(WindowModel 의 하위클래스)의 인스턴스가 됩니다. 편의상 openDialogWithSpec 은 메시지이름, 또는 SpecLayout 인스턴스로 특정 레이아웃을 인수로 가지는 openWindowWithSpec 과 동일한 변형을 가집니다.
DialogWindowModel 인스턴스(앞의 예제에서 diag)는 여러 가지 방법으로 구성할 수 있습니다. 사용자가 버튼을 클릭할 때 코드를 실행하려면, 인수 블록이 없는 okAction: 또는 cancelAction: 메시지를 보내면 됩니다. 블록이 "false"를 반환하면 창이 닫히지 않습니다(다른것이 반환되면 창은 닫힙니다). 이 창을 닫는 로직은 대화 상자 내용의 유효성 검사를 허용하며, 유효성 검사가 성공할 경우에만 닫히는것을 허용합니다.부연하자면, OK 버튼은 okButtonEnabled:false 메시지를 보내면 회색으로 표시되고, true 인수로 다시 활성화됩니다.
또한 Cancel 버튼을 클릭해서 상자를 닫으면 cancelled 메시지는 true 를 반환합니다.
전체 화면을 차지하기
마지막으로 UI 를 열어 Pharo 화면 전체를 관리 할 수도 있습니다. 다른 창은 없으며, UI 는 크기 조정이나 닫기 상자가 없는 전체 화면 또는 제목 표시 줄이 됩니다. 사실은 Pharo 전체 화면도 UI 창입니다.
Pharo 화면 전체를 가져 오려면 openWorldWithSpec 메시지를 보내십시오:
WindowExample new openWorldWithSpec.
halo 를 통해 UI 는 여전히 닫아질 수 있지만, 이전에 열려 있던 다른 창은 사라집니다. |
모달(modal) 창과 창 닫기
Windows 는 화면에 혼자있는 것이 아니며 영원히 존재할 수 있는것은 아닙니다. 여기서는 전체 사용자 인터페이스에 대한 완전한 제어권을 얻는 방법과 창을 닫는 로직에 연결하는 방법에 대해 설명합니다.
모달 창
모달 창은 전체 Pharo 사용자 인터페이스를 제어하는 창으로, 열려있는 동안 다른 창을 선택할 수 없습니다. 이러한 특성은 대화 상자에 특히 유용하지만, 다른 종류의 창에도 필요한 경우는 있습니다.
Spec 은 열려있는 ComposableModel 에 해당하는 WindowModel 에 modalRelativeTo : World 라는 메시지를 보냄으로써, 모든 창을 모달로 만들 수 있습니다. WidowModel 에서 핸들을 얻으려면 window 메시지를 보냅니다(UI 가 openWithSpec 으로 열리는 경우).
| we |
we := WindowExample new.
we openWithSpec.
we window modalRelativeTo: World
modalRelativeTo: 의 인수는 항상 현재 표시된 위젯 계층의 루트(World)가 되어야 합니다. |
창 닫기 방지
Spec 은 사용자가 닫기 상자를 클릭 할 때 창을 효과적으로 닫을 수 있는지 확인하는 기능을 제공합니다. 이 기능을 사용하려면 먼저 askOkToClose: true 를 ComposableModel 에 보내서 해당되는 기능을 켜야합니다. 예를 들어 다음처럼 WindowExample 을 변경하면됩니다:
WindowExample >> initializeWidgets
button1 := self newButton.
button2 := self newButton.
button1 label: '+'.
button2 label: '-'.
self askOkToClose: true.
그러나 이것만으로는 닫기 버튼의 동작은 변경되지 않으며, 계속 창을 닫을 수 있습니다. 이는 창을 닫을때 검사할 항목에 대한 구현 부분을 정의하지 않았기 때문입니다. 구현의 정의는, 아래처럼 ComposableModel 의 okToChange 메소드를 오버라이드 함으로써 쉽게 동작시킬 수 있습니다.
WindowExample >> okToChange
^false
이 메서드는 false 를 반환하기 때문에, 열려있는 WindowExample 윈도우의 닫기 버튼을 클릭해도 아무런 효과도 일어나지 않습니다. 이제 동작이 적용되는 창을 만들었습니다! 이 창을 닫으려면, 위 메서드의 구현을 true 를 반환하도록 변경해야 합니다(또는 코드를 지워도 되겠죠).
물론 앞의 okToChange 메서드 예제는 매우 단순하며 그리 유용한것도 아닙니다. 하지만 정말로 쓰려고 한다면, 창을 닫을때 검사 할 항목의 내용에 간단한 내용 대신에 어플리케이션에 관련된 로직(application-dependent logic)을 정의해야합니다. 시스템에는 참고할만한 okToChange 메소드의 예가 많이 있습니다.
창 닫기 지연
UI 의 WindowModel 에 whenClosedDo: 메시지를 보내서 창을 닫을 때마다 작업을 수행할 수도 있습니다. 예를 들어 다음의 예제에서는 UI 가 닫힐 때 전달할 작별 인사 메시지를 지정합니다.
| we |
we := WindowExample new.
we openWithSpec.
we window whenClosedDo: [ UIManager default inform: 'Bye bye!' ].
창 크기 및 장식
지금부터는, 창을 열기 전후에 창 크기를 조정 한 다음 창을 꾸미는 다른 컨트롤 위젯을 제거하는 방법에 대해 설명합니다.
초기 크기 설정 및 크기 변경
창이 열릴 때 창의 초기 크기를 설정하려면, 해당 ComposableModel 의 extent 메소드를 재정 의하여 Point 를 반환하거나, 인스턴스를 열기 전에 extent: 메시지를 보냅니다. 예를 들면 이렇습니다:
| we |
we := WindowExample new.
we extent: 300@80.
we openWithSpec
창이 열린 이후라면, UI 의 창에 extent: 메시지를 보내 크기를 조정할 수도 있습니다. 예를 들어, 당신은 예제의 initializeWidgets 메서드를 변경하여 클릭한 버튼에 따라 윈도우의 크기가 자동 조절되도록 할 수 있습니다.
WindowExample >> initializeWidgets
button1 := self newButton.
button2 := self newButton.
button1 label: '+'.
button2 label: '-'.
button1 action: [ self window extent: 500@200].
button2 action: [ self window extent: 200@100].
고정 크기
창의 크기는 고정되어 사용자가 측면이나 모서리를 드래그하여 크기를 조정할 수 없습니다. 이것을 설정하기 위해서는 기본 위젯 라이브러리와 대화해야 합니다(Pharo 5 에서는 Morphic). 예제(WindowModel)의 Morphic 창을 얻고 다음과 같이 크기가 변경되지 않도록(unresizable) 지시합니다.
| wewin |
wewin := WindowExample new openWithSpec.
wewin window beUnresizeable
창 장식 제거하기
장식이 없는 창 (예 : 컨트롤 위젯이 없는 창)을 만드는 것이 의미있는 경우도 있습니다. 현재 이 구성은 해당 창의 ComposableModel 에서 수행 할 수는 없지만, 기본 위젯 라이브러리가 허용 할 수도 있습니다. 아래에서는 예제의 Morphic 창을 가져와서 다른 컨트롤 위젯을 제거하는 방법을 보여줍니다.
| wewin |
wewin := WindowExample new openWithSpec.
wewin window
removeCollapseBox;
removeExpandBox;
removeCloseBox;
removeMenuBox
이 윈도우는 여전히 halo 메뉴를 사용하거나, WindowModel 인스턴스(에제의 wewin)에서 close 를 호출하여 닫을 수 있습니다. |
최종 세부 정보 : 제목, 아이콘 및 텍스트 정보
창에 대한 제목과 텍스트를 제공함으로써 window 에 대한 텍스트 정보를 제공 할 수 있습니다.
제목 설정 및 변경
기본적으로 새 창의 제목은 Untitled window 입니다. 이것은 title 메소드(ComposableModel 의 메서드)를 오버라이딩하고 제목으로 사용될 문자열을 리턴함으로써 변경될 수 있습니다. 예를 들어 다음처럼 예제 사용자 인터페이스의 제목을 지정할 수 있습니다:
WindowExample >> title
^ 'Click to grow or shrink.'
또한 (title 메소드를 지정하거나)새로운 제목의 title: 메시지를 인수로 UI 창에 보내서 UI 를 열면, 해당 제목을 설정할 수 있습니다. 예제는 아래와 같습니다:
| we |
we := WindowExample new.
we openWithSpec.
we window title: 'I am different!'
아이콘 설정하기
Pharo 메인창 하단에는 윈도우 작업 표시 줄이 있어서, 사용자가 각 창을 나타내는 버튼을 클릭하여 창 사이를 전환 할 수 있습니다. 이 창을 나타내는 버튼에는 윈도우의 종류를 나타내는 아이콘도 표시되고 있습니다. 이 아이콘은 Spec 을 통해 두 가지 다른 방법으로 구성 할 수 있습니다.
첫째, windowIcon: 메시지를 ComposableModel 에 보내면 아래 그림과 같이 각 창마다 아이콘을 설정할 수 있습니다. 창을 열기 전이나 후에 언제 메시지를 보내도 상관 없습니다.
| wm1 wm2 |
wm1 := WindowExample new.
wm1 openWithSpec.
wm1 windowIcon: (Smalltalk ui icons iconNamed: #thumbsDown).
wm2 := WindowExample new.
wm2 windowIcon: (Smalltalk ui icons iconNamed: #thumbsUp).
wm2 openWithSpec.
두 번째 방법으로, 아이콘은 아래와 같이 "windowIcon"메시지를 재정 의하여 변경할 수 있습니다. 아래의 코드는 Pharo 6 를 위한 것입니다. Pharo 5 에서 작동시키려면 self iconNamed: 를 Smalltalk ui icons iconNamed: 변경해 주시기 바랍니다.
WindowExample >> windowIcon
^ self iconNamed: #thumbsUp
windowIcon 메서드를 변경하면 작업 표시 줄이 주기적으로 새로 고쳐지기 때문에 열려있는 모든 창에 영향을 줍니다. 이 갱신 방식은 windowIcon: 을 창을 열기 전이나 후에 보낼 수 있는 이유가 되기도 합니다. |
about 텍스트 설정하기
윈도우의 텍스트를 설정하려면, 해당 ComposableModel 의 aboutText 메소드를 오버라이드해서 새로운 about 텍스트를 반환할 수도 있습니다. 예를 들어, 열기 전에 인스턴스에 aboutText: 메시지를 보내십시오.
| we |
we := WindowExample new.
we aboutText: 'Click + to grow, - to shrink.'.
we openWithSpec
결론
이 장에서는 Windows 와 관련이있는 Spec 의 기능을 다루었습니다. 먼저 창의 열기 및 닫기와 창을 대화 상자로 여는 방법에 대해 이야기 했습니다. 그 다음 창 크기와 장식 위젯을 구성했습니다. 그리고 창에 대한 작지만 중요한 세부 사항(창제목,아이콘,텍스트)으로 이 장을 끝냈습니다.