DeepintoPharo:Chapter 10

From 흡혈양파의 번역工房
Revision as of 12:40, 30 March 2014 by Onionmixer (talk | contribs) (이미지)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
제 10 장 Glamour

Glamour

Tudor Girba 참여 (tudor@tudorgirba.com)


브라우저는 복잡한 시스템이나 모델을 이해하는 데 결정적인 도구다. 또한 특정 영역을 탐색하고 상호작용하기 위한 툴이다. 각 문제 영역에는 기본 요소를 분석하고 해석하도록 돕기 위해 생성된 브라우저들이 많이 구비되어 있다. 이러한 브라우저와 관련된 문제는 보통 처음부터 (재)작성되기 때문에 생성 시 비용이 많이 들고 유지하기가 힘들다. 많은 프레임워크들이 대개는 사용자 인터페이스의 개발을 돕기 위해 존재하지만 브라우저의 생성을 간편화하는 데에는 제한된 지원만 제공하고 있다.


Glamour는 브라우저의 탐색 흐름을 설명하는 데 집중된 프레임워크다. 그 선언 언어 덕분에 Glamour는 데이터에 대한 새 브라우저를 재빠르게 정의하도록 해준다.


이번 장에서는 Glamour 프레임워크의 개요를 살펴보기 위해 예제로 소개한 브라우저의 생성을 먼저 상세히 살펴보고자 한다. 그 다음으로 세부적인 내용에 집중하겠다.


설치 및 첫 번째 브라우저

자신의 Pharo 이미지에 Glamour를 설치하기 위해서는 아래 코드를 실행하라.

Gofer new
    smalltalkhubUser: 'Moose' project: 'Glamour';
    package: 'ConfigurationOfGlamour';
    load.
(Smalltalk at: #ConfigurationOfGlamour) perform: #loadDefault.

그림 10.1: Glamour 구현으로 된 파일 파인더(file finder).


Glamour가 설치되었으니 Glamour의 선언 언어를 이용해 첫 브라우저를 빌드할 준비가 되었다. Apple의 Finder와 같은 파일 브라우저를 빌드하는 건 어떨까? 이 브라우저는 Miller Columns 브라우징 기법을 이용해 일련의 열로 계층 구조적 요소를 표시하여 빌드된다. 이러한 브라우저에서는 항상 앞 열에서 선택한 요소의 내용을 그 다음 열에 반영하고, 파일을 열면 첫 열의 내용이 선택되도록 되어 있는 것을 원리로 한다.


우리 예제처럼 파일 시스템을 탐색하는 경우, 브라우저는 특정 디렉터리의 엔트리(각 파일과 디렉터리)의 리스트를 첫 열에 표시하고, 사용자 선택영역에 따라 다른 열이 추가된다 (그림 10.1 참고).

  • 사용자가 디렉터리를 선택하면 다음 열(column)은 그 특정 디렉터리의 엔트리를 표시할 것이다.
  • 사용자가 파일을 선택하면 다음 열(column)은 파일의 내용을 표시할 것이다.


재귀성(recursion) 때문에 처음엔 복잡하게 보일 수도 있다. 하지만 Glamour는 Miller Columns 기반의 브라우저를 직관적으로 설명하는 방식을 제공한다. Glamour의 용어에 따르면 이 특별한 브라우저를 파인더(finder)라고 부르는데, 이는 Mac OS X에서 찾을 수 있는 Apple사의 Finder를 지칭한다. Glamour는 GLMFinder 클래스를 이용해 이러한 행위를 제공한다. 해당 클래스는 우리의 관심 영역, 즉 파일을 적절하게 열거하도록 인스턴스화되고 초기화되어야 한다.

| browser |
browser := GLMFinder new.
browser show: [:a |
    a list
        display: #children ].
browser openOn: FileSystem disk root.


이 단계에서 평문(plain) 파일을 선택하면 오류를 야기한다. 이러한 상황이 발생하는 이유와 수정 방법은 머지않아 이해하게 될 것이다.


이렇게 작은 코드 조각을 이용해 자신의 파일 시스템의 루트에서 발견되는 모든 엔트리의 (파일 또는 디렉터리) 리스트를 얻게 되고, 각 행은 파일이나 디렉터리를 나타낼 것이다. 디렉터리를 하나 클릭하면 해당 디렉터리의 엔트리가 다음 열에 표시될 것이다. 파일시스템 탐색 기능은 Filesystem 프레임워크에 의해 제공되는데, 이와 관련된 내용은 제 3장에서 충분하게 논한 바 있다.


하지만 이 코드에도 문제가 있다. 각 행은 엔트리의 전체 출력 문자열(print string)을 표시하는데 그 방식이 당신이 원하는 방식과 다를 수도 있다는 사실이다. 일반 사용자는 각 엔트리의 이름만 표시될 것이라고 예상한다. 이는 리스트를 맞춤설정하여 쉽게 해결이 가능하다.

browser show: [:a |
    a list
        display: #children;
        format: #basename ].


이런 방식으로 basename 메시지가 각 엔트리로 전송되어 이름을 얻을 것이다. 이는 파일과 디렉터리의 풀네임 대신 파일명만 표시하여 훨씬 읽기가 쉽다.


또 다른 문제로는, 코드가 파일과 디렉터리를 구별하지 않는다는 점을 들 수 있다. 파일을 클릭하면 브라우저는 파일이 이해하지 못하는 children이란 메시지를 파일로 전송하기 때문에 오류를 수신할 것이다. 이 문제를 수정하려면 선택된 요소가 파일일 경우 포함된 엔트리 리스트를 표시하지 않도록 해야 한다.

browser show: [:a |
    a list
        when: #isDirectory;
        display: #children;
        format: #basename ].


이러한 방식은 잘 작동하긴 하지만 사용자가 파일을 표현하는 행과 디렉터리를 표현하는 행을 구별하지 못한다는 단점이 있다. 여기서는 선택된 요소가 디렉터리일 경우 파일명 끝에 슬래시를 추가하는 등의 방법을 이용해 해결할 수 있다.

browser show: [:a |
    a list
        when: #isDirectory;
        display: #children;
        format: #basenameWithIndicator ].


파일일 경우 엔트리 내용을 표시하는 일은 절대로 하지 말아야 한다. 아래는 파일 브라우저의 최종 버전을 제공한다.

| browser |
browser := GLMFinder new
    variableSizePanes;
    title: 'Find your file';
    yourself.

browser show: [:a |
    a list
        when: #isDirectory;
        display: [:each | [each children ]
            on: Exception
            do: [Array new]];
        format: #basenameWithIndicator.
    a text
        when: #isFile;
        display: [:entry | [entry readStream contents]
            on: Exception
                do:['Can''t display the content of this file'] ] ].

browser openOn: FileSystem disk root.


위의 코드는 기존의 브라우저를 다양한 크기의 패인과 제목을 비롯해 디렉터리 엔트리, 접근 권한 처리, 파일 내용 읽기로 확장한다. 그 결과로 만들어진 브라우저는 그림 10.1에 소개하고 있다.


간략한 서론을 통해 Glamour를 설치하는 방법과 이를 이용해 간단한 파일 브라우저를 생성하는 방법을 소개해보았다.


Presentation, Transmission 그리고 Ports

이번 절은 Glamour 프레임워크의 실질적인 예제와 세부 내용을 제공한다.

실행 예제

다음 지침을 통해 간단한 스몰토크 클래스 탐색기(navigator)를 생성할 것이다. 그러한 탐색기는 많은 스몰토크 브라우저에서 사용되고 주로 4개의 패인으로 구성되는데 그림 10.2에 추상적으로 표시하였다.


클래스 탐색기는 다음과 같은 기능을 한다. 패인 1은 패키지의 트리 또는 리스트를 표시하는데, 각 패키지는 환경의 조직적 구조를 구성하는 클래스들을 포함한다. 패키지를 선택하면 선택된 패키지 내 모든 클래스의 리스트가 패인 2에 표시된다. 클래스를 하나 선택하면 패인 3에 모든 프로토콜(메서드를 그룹화하기 위한 구조체, 메서드 범주라고도 알려짐)을 표시하고, 패인 4에는 클래스의 모든 메서드가 표시된다. 패인 3에서 프로토콜을 하나 선택하면 해당 프로토콜에 속하는 메서드의 하위집합이 패인 4에 표시된다.

그림 10.2: 스몰토크 클래스 탐색기의 Wireframe 표현.


브라우저 시작하기

브라우저를 반복적으로 빌드하고 Glamour의 새 구조체를 서서히 소개해보겠다. 우선 패키지의 리스트에 새 브라우저를 열길 원한다. 예제에는 이전 파일 브라우저 이상의 코드를 수반할 것이기 때문에 전용(dedicated) 클래스에 코드 브라우저를 구현하고자 한다.


첫 번째 단계는 몇 가지 initial 메서드가 있는 클래스를 생성하는 것이다.

Object subclass: #PBE2CodeNavigator
    instanceVariableNames: 'browser'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'PBE2-CodeBrowser'

PBE2CodeNavigator class>>open
    ^ self new open

PBE2CodeNavigator>>open
    self buildBrowser.
    browser openOn: self organizer.

PBE2CodeNavigator>>organizer
    ^ RPackageOrganizer default

PBE2CodeNavigator>>buildBrowser
    browser := GLMTabulator new.


PBE2CodeNavigator open 을 실행하면 "a RPackageOrganizer" 텍스트로 된 새 브라우저를 열기만 할 뿐 다른 일은 전혀 하지 않는다. 이제 브라우저를 생성하기 위해 GLMTabulator 클래스를 사용함을 주목한다. GLMTabulator는 명시적 브라우저로서, 패인을 열과 행에 위치시키도록 해준다.


패키지의 리스트를 표시하기 위해 새 패인으로 브라우저를 확장한다.

PBE2CodeNavigator>>buildBrowser
    browser := GLMTabulator new.
    Browser
        column: #packages.

    browser transmit to: #packages; andShow: [:a | self packagesIn: a].

PBE2CodeNavigator>>packagesIn: constructor
    constructor list
        display: [:organizer | organizer packageNames sorted];
        format: #asString


Glamour 브라우저는 패인, 그리고 패인들 간 데이터 흐름과 관련해 구성된다. 예제로 제시한 브라우저는 패키지를 표시하는 하나의 패인만 포함한다. 데이터의 흐름은 transmissions를 이용해 명시된다. 이는 리스트 내 항목 선택과 같이, 브라우저의 그래픽 사용자 인터페이스에서 특정 내용이 변경되면 트리거된다. 선택된 패키지에 포함되어 있는 클래스를 표시함으로써 브라우저를 더 흥미롭게 만들고자 한다 (그림 10.3 참고).

PBE2CodeNavigator>>buildBrowser
    browser := GLMTabulator new.
    browser
        column: #packages;
        column: #classes.

    browser transmit to: #packages; andShow: [:a | self packagesIn: a].
    browser transmit from: #packages; to: #classes; andShow: [:a | self classesIn: a].

PBE2CodeNavigator>>classesIn: constructor
    constructor list
        display: [:packageName | (self organizer packageNamed: packageName)
            definedClasses]


위의 리스팅은 Glamour의 거의 모든 핵심 언어 구조체를 표시한다. 후에 패인의 참조가 가능하길 원하므로 "packages"와 "classes"라는 이름을 따로 부여하고, column: 키워드를 이용해 열(column)에 정렬한다. 이와 유사한 row: 이라는 키워드도 존재하는데, 이는 패인을 행(row)으로 조직할 수 있다.


transmit:, to:, from: 키워드는 transmission, 즉 한 패인에서 다른 패인으로의 정보 흐름을 정의하는 직접 연결을 생성한다. 예제의 경우, packages 패인으로부터 classes 패인으로 연계(link)를 생성한다. from: 키워드는 전송의 origin(출발지)를 나타내고, to: 는 destination(목적지)를 나타낸다. 더 이상 구체적인 내용이 표기되지 않는 이상 Glamour는 출발지를 명시된 패인의 selection(선택 영역)을 가리키는 것으로 가정한다. Origin 패인의 다른 측면들을 명시하는 방법과 여러 개의 origin을 사용하는 방법은 아래에서 설명하겠다.

그림 10.3: 2-pain 브라우저. 왼쪽 패인에서 패키지를 선택하면 패키지에 포함된 클래스가 오른쪽 패인에 표시된다.


마지막으로, andShow: 는 연결이 활성화되거나 transmitted(전송)되었을 때 destination 패인에 무엇을 표시할 것인지를 명시한다. 예제에서는 왼쪽에서 선택한 패키지에 포함된 클래스의 리스트를 표시하고자 한다.


display: 키워드는 제공된 블록을 단순히 presentation 내부에 저장한다. 블록은 후에 presentation을 on-screen에 표시해야 하는 경우에만 평가될 것이다. 명시적 디스플레이 블록이 명시되지 않은 경우 Glamour는 일반적인 방식으로 객체를 표시하고자 시도할 것이다. 리스트를 표시하는 예제에서 이는 displayString 메시지가 객체로 전송되어 표준 문자열 표현을 검색할 것임을 뜻한다. 이미 살펴보았듯 이러한 행위는 format: 를 이용해 변경할 수 있다.


display: 를 비롯해 when: 조건을 명시하여 연결의 적용 가능성을 제한하는 것도 가능하다. 기본적으로는 항목이 실제로 선택되어야 한다는 것이 유일한 조건으로, 디스플레이 변수 인자가 null이 아닌 값이어야 한다.


또 다른 Presentation

지금까지 패키지는 시각적으로 평평한 리스트로 표현되었다. 하지만 패키지는 본래 해당하는 클래스 범주로 구조화된다. 이러한 구조를 활용하기 위해 리스트를 패키지의 트리 표현으로 대체하겠다.

PBE2CodeNavigator>>packagesIn: constructor
    constructor tree
        display: [ :organizer | (self rootPackagesOn: organizer) asSet sorted ];
        children: [ :rootPackage :organizer | (self childrenOf: rootPackage on: organizer)
            sorted ];
        format: #asString

PBE2CodeNavigator>>classesIn: constructor
    constructor list
    when: [:packageName | self organizer includesPackageNamed: packageName ];
    display: [:packageName | (self organizer packageNamed: packageName)
        definedClasses]

PBE2CodeNavigator>>childrenOf: rootPackage on: organizer
    ^ organizer packageNames select: [ :name | name beginsWith: rootPackage , '-' ]

PBE2CodeNavigator>>rootPackagesOn: organizer
    ^ organizer packageNames collect: [ :string | string readStream upTo: $- ]


트리 표현은 트리 내에 주어진 항목의 자식들을 검색하는 방법을 명시하기 위해 하나의 선택자나 블록을 취하는 children: 인자를 이용한다. 각 패키지의 자식들은 이제 트리 표현으로 선택되기 때문에 패키지 계층구조의 루트만 display: 인자로 전달해야 한다.


이 시점에서 Pane 3를 추가하여 메서드 범주를 열거하는 것도 가능하다 (그림 10.4). 아래 리스팅은 지금까지 논한 요소로 구성된다.

PBE2CodeNavigator>>buildBrowser
    browser := GLMTabulator new.
    browser
        column: #packages;
        column: #classes;
        column: #categories.
    browser transmit to: #packages; andShow: [:a | self packagesIn: a].
    browser transmit from: #packages; to: #classes; andShow: [:a | self classesIn: a].
    browser transmit from: #classes; to: #categories; andShow: [:a | self categoriesIn: a].

PBE2CodeNavigator>>categoriesIn: constructor
    constructor list
        display: [:class | class organization categories]


위의 변경 내용에서 도출된 브라우저는 그림 10.4에 실려 있다.


다수의 Origin

메서드의 리스트를 Pane 4로서 추가하는 데에는 조금 더 많은 조직이 수반된다. 메서드 범주를 선택하면 해당 범주에 속하는 메서드만 표시하길 원할 것이다. 어떤 범주도 선택되지 않을 경우 현재 클래스에 속한 모든 메서드가 표시된다.


이는 메서드 패인으로 하여금 클래스 패인과 범주 패인의 선택 영역에 의존하도록 만든다. 다중 origin은 아래와 같이 from: 키워드를 여러 번 이용해 정의할 수 있다.

그림 10.4: 선택된 클래스의 메서드 범주 리스트와 패키지를 표시하는 트리가 포함된 향상된 클래스 탐색기.

PBE2CodeNavigator>>buildBrowser
    browser := GLMTabulator new.
    browser
        column: #packages;
        column: #classes;
        column: #categories;
        column: #methods.

    browser transmit to: #packages; andShow: [:a | self packagesIn: a].
    browser transmit from: #packages; to: #classes; andShow: [:a | self classesIn: a].
    browser transmit from: #classes; to: #categories; andShow: [:a | self categoriesIn: a].
    browser transmit from: #classes; from: #categories; to: #methods;
        andShow: [:a | self methodsIn: a].

PBE2CodeNavigator>>methodsIn: constructor
    constructor list
        display: [:class :category |
            (class organization listAtCategoryNamed: category) sorted].
    constructor list
        when: [:class :category | class notNil and: [category isNil]];
        display: [:class | class selectors sorted];
        allowNil


리스팅에 몇 가지 새로운 프로퍼티가 보인다. 첫째, 다중 origin은 display: 와 when: 절에 사용된 블록의 인자 개수에 반영된다. 둘째, 하나 이상의 표현을 사용하고 있어서, 해당 전송이 fired되면 Glamour는 정의된 순서에 매칭하는 조건의 표현을 모두 표시한다.


첫 번째 표현에서는 모든 인자가 정의되어 (null이 아닌 값) 있을 때 조건이 매칭하는데, 모든 표현에서 기본 값이다. 두 번째 조건은 범주가 정의되어 있지 않고 클래스가 정의되어 있을 때에만 매칭한다. origin이 정의되어 있지 않을 때에도 표현을 표시해야 하는 경우, 표시된 바와 같이 allowNil 을 사용하는 것이 유용하겠다. 따라서 디스플레이 블록에서 범주를 생략할 수 있다.


완성된 클래스 탐색기는 그림 10.5와 같은 모습이다.

그림 10.5: 완전한 코드 탐색기. 메서드 범주가 선택되지 않은 경우 클래스의 모든 메서드가 표시된다. 선택된 경우 해당 범주에 속하는 메서드만 표시된다.


Ports

transmissions는 패인으로 연결된다고 언급한 적이 있는데, 전적으로 옳은 말은 아니다. 좀 더 정밀하게 말해, transmissions는 ports(포트)라고 불리는 패인의 프로퍼티로 연결된다. 그러한 포트는 패인의 상태에 대한 특정 측면이나 그에 포함된 표현을 나타내는 값과 이름으로 구성된다. 사용자가 명시적으로 포트를 명시하지 않으면 Glamour는 기본적으로 selection 포트를 이용한다. 그 결과 아래와 같은 두 개의 문은 동일하다고 볼 수 있다.

browser transmit from: #packages; to: #classes; andShow: [:a | ...].
browser transmit from: #packages port: #selection; to: #classes; andShow: [:a | ...].


구성하기(composing)와 상호작용

Browsers 재사용하기

Glamour의 강점 중 하나로 리스트나 트리와 같은 primitive 표현 대신 브라우저를 사용한다는 점을 들 수 있다. 이는 브라우저를 구성하고 높은 내포(nest)의 가능성을 제시한다.


다음 예제는 그림 10.6과 같은 editor 클래스를 정의한다. 패인 1부터 4까지는 앞에서 설명한 패인과 같다. 패인 5는 패인 4에서 현재 선택된 메서드의 소스 코드를 표시한다.

그림 10.6: 스몰토크 editor 클래스에 대한 Wireframe 표현.


PBE2CodeEditor라는 새 클래스가 해당 editor를 구현할 것이다. editor는 패인 1부터 4까지 표현을 이전에 구현된 PED2CodeNavigator로 위임할 것이다. 이를 위해서는 먼저 기존의 탐색기가 구성된 브라우저를 반환하도록 만들어야 한다.

PBE2CodeNavigator>>buildBrowser
    ...
    "new line"
    ^ browser


다음으로, 아래와 같이 새 editor 브라우저에서 탐색기를 재사용할 수 있다.

Object subclass: #PBE2CodeEditor
    instanceVariableNames: 'browser'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'PBE2-CodeBrowser'.

PBE2CodeEditor class>>open
    ^ self new open

PBE2CodeEditor>>open
    self buildBrowser.
    browser openOn: self organizer

PBE2CodeEditor>>organizer
    ^ RPackageOrganizer default

PBE2CodeEditor>>buildBrowser
    browser := GLMTabulator new.
    browser
        row: #navigator;
        row: #source.

    browser transmit to: #navigator; andShow: [:a | self navigatorIn: a ].

PBE2CodeEditor>>navigatorIn: constructor
    constructor custom: (PBE2CodeNavigator new buildBrowser)


위의 리스팅은 우리가 리스트나 다른 타입의 표현을 사용할 때와 정확히 같은 방식으로 사용함을 보여준다. 사실 브라우저는 표현의 한 가지 타입이다.


PBE2CodeEditor open을 평가하면 탐색기를 윗부분에 삽입하고 아랫부분엔 빈 패인을 가진 브라우저가 열린다. 소스 코드는 아직 표시되지 않는데, 패인 간 어떤 연결도 구축되지 않았기 때문이다. 소스 코드는 텍스트 패인으로 탐색기를 연결함으로써 얻으며, 선택된 메서드명과 그것이 정의된 클래스도 필요하다. 이러한 정보는 navigator 브라우저에서만 정의되기 때문에 먼저 sendToOutside:from: 을 이용해 외부 세계로 내보내야 한다. 이를 위해 아래의 행을 codeNavigator로 추가한다.

PBE2CodeNavigator>>buildBrowser
    ...
    browser transmit from: #classes; toOutsidePort: #selectedClass.
    browser transmit from: #methods; toOutsidePort: #selectedMethod.

    ^ browser


그러면 포함하는 패인의 selectedClass 포트와 selectedMethods 포트로 클래스와 메서드 내에서 선택한 영역이 전송될 것이다. 이 방법이 아니라면, 코드 에디터에서 navigationIn: 메서드로 아래와 같은 행을 추가할 수도 있는데, Glamour에는 차이가 없다.

PBE2CodeEditor>>navigatorIn: constructor
    "Alternative way of adding outside ports. There is no need to use this
    code and the previous one simultaneously."

    | navigator |
    navigator := PBE2CodeNavigator new buildBrowser
        sendToOutside: #selectedClass from: #classes -> #selection;
        sendToOutside: #selectedMethod from: #methods -> #selection;
        yourself.

    constructor custom: navigator


하지만 해당 인터페이스의 재사용까지 장려하기 위해서는 코드 편집기 내부보다는 코드 navigator 측에서 인터페이스를 분명히 정의하는 편이 더 합리적이라고 생각한다.

따라서 코드 에디터 예제를 아래와 같이 확장한다.

PBE2CodeEditor>>buildBrowser
    browser := GLMTabulator new.
    browser
        row: #navigator;
        row: #source.

    browser transmit to: #navigator; andShow: [:a | self navigatorIn: a].
    browser transmit
        from: #navigator port: #selectedClass;
        from: #navigator port: #selectedMethod;
        to: #source;
        andShow: [:a | self sourceIn: a].

PBE2CodeEditor>>sourceIn: constructor
    constructor text
        display: [:class :method | class sourceCodeAt: method]


이제 선택된 메서드의 소스 코드는 모두 확인이 가능하며, 앞에서 이미 작성한 클래스 탐색기를 이용하여 modular 브라우저를 생성하였다. 리스팅이 설명한 대로 구성된 브라우저는 그림 10.7에서 소개한다.


액션

영역(domain)의 탐색은 흥미로운 요소를 찾는 데 필수적이다. 하지만 이용 가능한 액션의 집합을 갖는 것은 영역과 상호작용을 허용하는 데에 꼭 필요하다. 액션은 정의되어 표현과 연관될 수 있다. 액션은 키보드 단축키를 누르거나 컨텍스트 메뉴에서 엔트리를 클릭할 때 평가되는 블록이다. 액션은 표현으로 전송되는 act:on: 을 통해 정의된다.

PBE2CodeEditor>>sourceIn: constructor
    constructor text
        display: [:class :method | class sourceCodeAt: method ];
        act: [:presentation :class :method | class compile: presentation text] on: $s.

그림 10.7: 선택된 메서드의 소스를 표시하기 위해 앞에서 설명한 클래스를 재사용하는 구성(composed) 브라우저.


on: 으로 전달된 인자는 해당하는 표현이 포커스를 받았을 때 액션을 트리거하는 데 사용되어야 하는 키보드 단축키를 명시하는 문자다. 문자를 메타 키, 가령 콤마, 컨트롤, 알트와 같은 키와 결합해야 하는지 여부는 플랫폼에 따라 다르므로 명시되지 않아도 된다. act: 블록은 상응하는 표현을 첫 번째 인자로서 제공하는데, 결합된 텍스트나 현재 선택 영역과 같은 다양한 프로퍼티를 알아내는 데 사용할 수 있다. 다른 블록 인자들은 from: 이 정의하는 대로 들어오는 origin이며, display: 와 when: 의 인자와 같다.


액션은 컨텍스트 메뉴로 표시되기도 한다. 이를 위해 Glamour는 act:on:entited: 와 act:entitled: 메시지를 제공하는데, 이러한 메시지에서 마지막 인자는 메뉴의 엔트리로 표시되어야 하는 문자열에 해당한다. 예를 들어, 아래 코드 조각은 위의 예제를 확장시켜 현재 메서드를 클래스로 "저장"하기 위한 컨텍스트 메뉴 엔트리를 제공한다.

...
    act: [:presentation :class :method | class compile: presentation text]
    on: $s
    entitled: 'Save'


컨텍스트 메뉴는 왼쪽에 위치한 텍스트 패인 위의 아래 방향 삼각형(triangle downward-oriented)을 통해 접근 가능하다.


다수의 Presentation

개발자들은 특정 객체의 표현을 하나 이상 제공하고 싶을 때가 있다. 앞에서 제시한 코드 브라우저의 예제에서 가령 클래스를 리스트 뿐만 아니라 그래픽 표현으로서 표시하길 원한다고 가정하자. Glamour는 Mondrian visualization engine (12장에서 소개)을 이용해 생성된 시각화의 표시 및 상호작용을 지원한다. 2차 표현을 추가하기 위해서는 아래와 같이 using: 블록에서 정의하면 된다.

PBE2CodeNavigator>>classesIn: constructor
    constructor list
        when: [:packageName | self organizer includesPackageNamed: packageName ];
        display: [:packageName | (self organizer packageNamed: packageName)
            definedClasses];
        title: 'Class list'.

constructor mondrian
    when: [:packageName | self organizer includesPackageNamed: packageName];
    painting: [ :view :packageName |
        view nodes: (self organizer packageNamed: packageName)
            definedClasses.
        view edgesFrom: #superclass.
        view treeLayout];
    title: 'Hierarchy'


Glamour는 탭 레이아웃의 도움을 받아 같은 패인에 다수의 표현을 구별한다. Mondian 표현의 모양은 코드 에디터에 내포되어 있는데, 이는 그림 10.8에서 소개한다. title: 절은 표현을 렌더링하는 데에 사용되는 탭의 이름을 설정한다.

그림 10.8: 간단한 클래스 리스트와 함께 Mondrian 표현을 표시하는 코드 에디터.


기타 브라우저

앞서 언급한 row: 와 column: 키워드를 이용해 커스텀 레이아웃을 생성하는 기능을 본따 명명된 GLMTabulator를 기본적으로 이용해왔다. 사용자는 추가 브라우저를 제공하거나 작성할 수 있다. 브라우저 구현은 두 가지 범주로 다시 나뉘는데, 명시적 패인을 가진 브라우저가 첫 번째 범주로서 사용자가 명시적으로 선언하는 것이며, 두 번째는 암시적 패인(implicit pane)이 되겠다.


GLMTabulator는 명시적 패인을 사용하는 브라우저의 예에 해당한다. 암시적 브라우저를 이용할 경우 패인을 직접적으로 선언하지 않고 브라우저가 패인과 패인 간 연결을 내부적으로 생성한다. 그러한 브라우저의 예로 Finder를 들 수 있는데, 이와 관련된 내용은 10.1절에서 논한 바 있다. 패인이 알아서 생성되기 때문에 우리는 from:to: 키워드를 사용하지 않아도 되며, 단순히 표현을 명시하기만 하면 된다.

browser := GLMFinder new.

browser list
    display: [:class | class subclasses].

browser openOn: Collection


위의 리스팅은 브라우저를 생성하고 (그림 10.9에 표시) 열어서 Collection의 서브클래스 리스트를 표시한다. 리스트에서 항목을 선택하면 브라우저가 오른쪽으로 확장되면서 선택된 항목의 서브클래스를 표시한다. 선택할 내용이 남아 있는 한 무한으로 계속될 수 있다.

그림 10.9: Miller Columns 스타일 브라우징을 이용한 서브클래스 탐색기.


다른 유형의 브라우저를 살펴보기 위해서는 GLMBrowser 클래스의 계층구조를 탐색해보라.


요약

본 장을 통해 Glamour 브라우저 프레임워크를 간략하게 소개하였다. Glamour는 기본적으로 평범한 객체로 구성된 임의의 영역을 탐색하고 상호 작용할 수 있게 해주는 빌드 툴을 빌드하는 데에 사용된다.

  • GLMTabulator는 위젯을 열과 행으로 정렬한 일반 브라우저다.
  • 열은 기호 이름을 인자로 한 column: 을 연속으로 전송함으로써 정의된다.
  • 데이터는 transmit from: #source; to: #target 을 이용해 설정된 transmissions를 따라 흐른다.
  • transmission는 여러 개의 소스를 가질 수 있다.
  • 리스트 패인과 텍스트 패인은 브라우저로 list와 test를 각각 전송하여 얻는다. 내용은 display: 를 이용해 설정되고, 항목들은 format: 를 이용해 포맷팅된다.
  • Ports는 브라우저의 컴포넌트 인터페이스를 정의한다. 이는 재사용과 삽입(embedding)을 수월하게 해준다.
  • 상호작용은 위젯으로 act: 를 전송하여 정의되는 액션과 관련해 정의된다.
  • Glamour는 다수의 표현을 지원한다.
  • Glamour는 일반 용도의 그래픽 사용자 인터페이스를 빌드하기 위해 만들어진 것이 아니다.


이번 장은 Glamour의 전반적인 개요를 제공하는 것이 목적이 아니라 독자에게 필자의 접근법의 사용법과 그 의도를 소개하기 위해 작성되었다. Glamour 와 그 개념 및 구현을 광범위하게 살펴보기 위해서는 Moose 에서 이를 집중적으로 다룬 장을 참고한다[1].


Notes