Swing/SWT 통합(번역)
이 글은 eclipse.org의 ECLIPSE CORNER ARTICLES 코너의 Swing/SWT Integration 기사를 번역한 것으로 Swing과 SWT 통합시 주의할 사항과 버그에 대한 대처 방안과 그 해결 방법을 기술한 글이다. 원문은 http://www.eclipse.org/articles/article.php?file=Article-Swing-SWT-Integration/index.html에 있다.
RCP와 함께 Swing 컴포넌트를 사용하는 것은 두개의 커다란 UI 툴킷을 하나의 어플리케이션에 공존하게 하는 것이고 전체 어플리케이션의 일관성을 보장하기 위해서는 어느 정도의 통합을 필요로 한다.
SWT는 Swing/SWT 통합을 위해 기본구조로서 SWT/AWT 브릿지(Bridge)를 제공한다. 브릿지는 RCP 어플리케이션에서 사용할 Swing 컴포넌트를 끼워넣는 방법으로 유일한 첫 단계이다. 이 기사의 많은 부분을 효과적인 통합에 필요한 추가적인 연습을 설명하는 것에 할예하고 있다. 이러한 연습이 없으면, SWT/AWT 브릿지는 사용하기에 충분하지 못하다. 연습을 함으로서, 여러분은 성공적으로 RCP 어플리케이션에서 Swing 컴포넌트를 통합할 수 있을 것이다.
두 가지의 독립된 UI 툴킷을 통합하는 것은 복잡한 작업이다, 그리고 그것은 거의 완벽한 결과를 생산하기 어렵다. 이 기사는 실용적인 기술(몇몇의 해킹)에 집중하여 실제 어플리케이션에 충분히 효과적으로 통합하도록 하고있다.
복잡성에도 불구하고, 아래에 묘사된 대부분의 사례들은 Swing 컴포넌트를 끼워넣을때 사용될 유일 공통 클래스로 캡슐화 될 수 있다. 구현을 지원하기위한 부가적인 헬퍼 클래스 들이 존재한다. 예제는 이 기사에 포함되어 있다. 부록 A, 예제 코드를 보면 더 많은 정보가 있다.
위 그림에 약간의 즉각적이고 명확한 문제가 존재한다. Swing JTable(우상단)의 헤더와 스크롤바가 SWT-기반의 Error Log 뷰와 다르게 보인다.
이 예에서, JTable은 표준 스윙 Cross-Platform(Metal) 룩앤필로 전시되어 있다. 반면에, SWT 컴포넌트들은 네이티브 룩앤필(LookAndFeel)로 된 아래의 네이티브 위젯을 사용한다. Swing의 룩앤필이 반드시 네이티브 플렛폼에 맞게 바뀌어야한다. SWT/AWT 브릿지 자체로는 자동으로 네이티브 룩앤필로 설정되지 않기 때문에, 여러분 스스로 그렇게 해줘야 한다. 예를 들어
임베디드 프레임은 Window(java.awt.Windows의 서브클래스)이다. 이것은 자원을 소비한다. 이러한 이유로 우리는 임베디드 프레임의 대체로서 작은 컴포넌트 보다 더 넓은 컴포넌트의 사용을 장려한다.
추가적인 조치없이, SWT/AWT 브릿지를 사용하는것은 어플리케이션이 리사이즈되는 동안 임베디드 프레임에 과도한 깜박임을 유발하게 된다. 이유는 아래와 같다.
이러한 효과는 부모 SWT 컴포지트에 리사이즈 리스너를 추가함으로서 제거될 수 있다. 컴포지트가 크게 리사이즈 되어, 그려지지 않는 영역이 노출되었을때 리스너는 측시 배경색으로 그 영역을 채워주게 된다. 그러면 치즈(cheese)는 즉시 제거되고, 임베디드 Swing 컴포넌트가 다시 그려질때 어떤 지연없이 꾸며져 향상된 화면을 제공한다.
또 다른 Swing/SWT 통합의 고려는 각 툴킷에서 툴킷으로 탭(Tab)키가 순회할 때의 고려사항이다. 이 문제를 해결하려면 SWT/AWT 브릿지의 AWT 쪽에서 추가적인 작업이 필요하다. 적합한 탭 순회를 달성하려면, 임베디드 AWT 프레임을 위한 java.awt.FocusTraversalPolicy의 서브 사용자 클래스를 구현하라. 프레임 안에서 탭핑(tabbing)할때 사용자 정책이 표준정책을 위임하게 된다. 하지만 그것은 반드시 SWT 컴포넌트로 컨트롤을 다음과 같을때 이동한다.
사용자 정책에서 앞으로 뒤로 순회하는 것을 구현하는 것은 간단하다. 하지만, 적절하게 getDefaultComponent 메소드를 구현하는 것은 까다롭다. 그것은 임베디드 AWT 프레임이 포커스를 가지지 않았을때 반드시 null을 리턴해야만 한다. 이 행동은 AWT가 어떻게 메소드를 콜(Call)하는 것에 대해서 핵킹한 것에 기반을 두고있고, 그것은 SWT의 순회 후에 AWT로 즉각적으로 포커스가 이동하는 것을 피하기 위해 필요하다. 더 많은 정보를 원하면, 부록의 EmbeddedChildFocusTraversalPolicy 클래스의 코맨트를 참조하라.
포커스 순회에 대해 더많은 정보를 위해선 AWT Focus Subsystem이라는 Swing 도큐먼트를 보라.
모달 다이얼로그가 Swing 컴포넌트에서 오픈되었을때, 전체 어플리케이션이 모달이 되어야 한다. SWT와 AWT는 각각의 이벤트 쓰레드를 가지고 있기 때문에, 그러한 다이얼로그는 기본적으로 모달이 아니다. SWT 이벤트 쓰레드는 Swing 모달 다이얼로그가 보여지는 동안
명시적으로 동작하지 않아야 한다. 이러한 효과는 Swing 다이얼로그가 보이는 동안 SWT 모달 다이얼로그를 오픈하는 것으로 쉽게 가능하다.
Linux/GTK에서, 제로(Zero) 사이즈의 SWT 다이얼로그도 작은 점으로 보일것이다. focusGained()에 다음의 코드를 추가하는것으로 그것을 숨길수 있다. 다음의 코드는 윈도우에서는 필요치 않고 실제적으로 번쩍임(flash)를 유발할 것이다, 그래서 단지 필요할 때에만 콜해야 한다.
위에 보여진 코드는 정확한 모달을 보장하지만, 어떻게 호출해야 할까? 가장 쉬운 접근법은 Swing 모달 다이얼로그가 생성될때 마다 호출하는 것이다. 하지만 이 방법은 Swing 컴포넌트 라이브러리가 수정 될수 없거나, Swing 컴포넌트 라이브러리로 부터 SWT 코드에 의존성을 끼워넣을 수 없다면 동작하지 않을 것이다.
대안적으로, 모든 AWT 윈도우 이벤트를 설정하는 것으로 보다 명확하게 정확한 모달을 강제할 수 있다. Swing 모달 다이얼로그가 오픈되거나 보여지는 것이 탐지될 때마다, SWT 모달 윈도우가 오픈되어야 한다. 리스너의 예는 아래와 같고, 예제 코드에 완전히 구현되어 있다. 부록 A Example Code 에 더 많은 정보가 존재한다.
컨텍스트 메뉴가 임베디드 AWT 프레임에 전시될때, 프레임의 바깥쪽을 클릭할때 메뉴가 사라지지 않는 것을 볼 수 있다. 이러한 AWT의 제약사항은 잘 알려진 것이다, 하지만 같은 SWT 쉘에서 다중의 팝업이 보여지는 것은 임베디드 프레임에서 눈에 띄게 보기 좋지 않다.
이 문제에 대한 부분적인 해결방법이 존재한다.
이러한 해결책에도 불구하고, 몇가지 이슈들이 남겨져 있다; 예를들어 워크벤치 윈도우 타이틀바에서의 사용자작업(상호작용)에서 Swing쪽 팝업이 해체되지 않는다.
사용자가 윈도우 컨트롤 패널을 통해 폰트 설정을 변경했을때, 그 변경 내용이 항상 적합하게 Swing 컴포넌트로 전파되지 않는다. 이 문제는 SWT 컴포넌트가 실제로 이러한 변화를 인식할때 SWT와 Swing 컴포넌트를 통합에서 특히 보여진다. 이러한 문제를 해결하려면, Swing 윈도우 룩앤필에서 시스템 폰트가 변경 될때 수동으로 폰트를 변경해야 한다.
다행이, Eclipse 3.2에서, 어플리케이션이 시스템 설정을 변경할때 새로운 SWT 이벤트가 추가 되었다. 그래서, Swing 폰트 변경은 아래와 같이 할수 있다.
만일 여러분이 RCP 어플리케이션의 글로벌 액션과 임베디드 Swing 컴포넌트의 핸들러에 같은 키스트로크를 맵핑했다면, 예상치 못한 결과가 있을 수 있다. org.eclipse.ui.bindings를 통해 정의된 키스트로크의 확장 포인트는 Swing 컴포넌트가 포커스 되었을지라도, Swing 컴포넌트에 정의된 것보다 상위에 있을 것이다. 이 경우에, RCP 바인딩은 디스플레이 필터를 통해 관리되고, 그래서 Swing 컴포넌트에 도달하기 전에 소모될 것이다. 결과적으로, 임베디드 컴포지트 레벨에서 이 문제를 피할 수 있는 단순하고, 일반적인 해결방법은 존재하지 않는다. 대신 다음과 같은 방법으로 충돌을 피할 수 있다.
윈도우 JRE 1.5와 이전버전에서, Swing 시스템 룩앤필은 완벽하게 클리어타입 안티알리아싱(Anti-Aliasing)을 지원하지 않는다. 기본적으로, 클리어타입이 활성화되고 같은 폰트와 사이즈가 Swing과 SWT 컨트롤들에 선택 되었을때, 폰트는 다르게 보여진다. 부분적인 해결책은 swing.aatext 속성을 "true"로 설정하는 것으로 가능하다. 이런 미표기 속성은 Java6 이전의 환경에서 폰트 안티알리아싱을 가능하게 하지만, 그것은 단지 안티알리아싱을 켜기만 한다. 그것은 현재의 윈도우즈 시스템 셋팅값들과 동기화되지는 않는다. Swing은 자신만의 안티알리아싱 알고리즘을 가지고 있지만, 네이티브 디스플레이 폰트와 미세한 차이가 보여진다. 이 문제는 Java6에서 해결되었다. 아래의 스크린샷은 이러한 문제를 보여준다. 여기에 두개의 SWT 기반의 뷰가 왼쪽에 있고, Swing 테이블 컴포넌트가 오른쪽에 있다.
부록 A, 예제코드는 이러한 모든 추가적인 연습을 보여주는 예제 코드를 포함하고 있다.
Swing/SWT Integration
Swing/SWT 통합
요약
Swing과 SWT는 때때로 엄중하게 경쟁하는 기술들로 보인다. 몇몇의 사람은 UI 툴킷이 클라이언트 어플리케이션에 대해 독점적으로 사용되어야 한다는 강한 주장을 하고있다. 하지만, 실제에서, 이데올로기적으로 극단적인 것은 때때로 실용적이지 못하다. 몇몇의 유효한 사례들에서 두 가지 기술은 하나의 어플리케이션에서 공존을 필요로 한다. 두가지 툴킷을 섞는것은 간단하지 않지만 그렇게 할 수 있고 그 두가지 툴킷이 부드럽게 통합되는것도 할 수 있다. 이 기사는 훌륭한 Swing/SWT 통합을 위해서 필요로한 단계들에 대해 논의한다. 이것은 기존재하는 Swing 컴포넌트를 SWT-기반의 Rich Client Platform 어플리케이션으로 끼워넣는 사례에 포커스를 맞추고 있다.
By Gordon Hirsch, SAS Institute Inc.
Copyright © 2007 SAS Institute Inc. Made available under the EPL v1.0
Introduction
소개
현존하는 많은 Java 클라이언트들은 Swing 컴포넌트에 많이 투자한 것을 보여주고 있다. 이러한 클라이언트들을 이클립스 Rich Client Platform(RCP)로 이동을 필요로하는 요인이 존재하는 반면에, 현재의 방대한 어플리케이션의 전체이관(Full Migration)은 많은 비용이 들수 있다. 몇몇의 Swing 컴포넌트들의 유지하는 하는 것으로, 이관(Migration)의 초기 비용이 줄어들 수 있다. 시간이 지나면서, Swing 컴포넌트는 점차 SWT로 변환 될 것이고, 많일 그것이 필요 하다면 그렇게 될 것이다.
예를 들어, SAS(아래 그림)에서 우리는 현재 어플리케이션에서 볼 수있는 Swing의 JTable을 상속한 향상된 테이블을 가지고 있다. 이 컴포넌트를 SWT로 변환하려면 큰 노력이 요구된다. 동일한 SWT 테이블을 기다리는 대신, 우리는 아래의 스크린샷에서 보여지는 것처럼, RCP 어플리케이션에서 Swing 테이블 컴포넌트를 그대로 사용할 수 있다. 아래는 Windows XP 플랫폼에서 구동하는 RCP 어플리케이션을 보여준다.
Figure 1. Embedding Swing Table Components

또한 그래프와 차트를 보여주는 복잡한 Swing 컴포넌트들의 목록이 있다. 다시, Swing과 SWT를 혼합하는 것으로, 우리는 우리가 즉각 RCP 어플리케이션으로 이관할 필요가 있는 Swing 컴포넌트를 변환하는 것을 고려할 수 있다. 아래의 스크린샷은 Swing 기반의 그래프 컴포넌트로된 또 다른 RCP 어플리케이션을 보여준다. 이미지에서 그래프 이외의 것은 모두 SWT 컴포넌트이다. (이 어플리케이션은 RCP 어플리케이션 처럼 보이지 않지만, RCP 어플리케이션이다. 일반적이지 않게 보이는 이유는 Eclipse 프리젠테이션 API와 다른 Advanced Workbench의 사용 때문이다.)
Figure 2. Embedding Graphical Swing Components

RCP와 함께 Swing 컴포넌트를 사용하는 것은 두개의 커다란 UI 툴킷을 하나의 어플리케이션에 공존하게 하는 것이고 전체 어플리케이션의 일관성을 보장하기 위해서는 어느 정도의 통합을 필요로 한다.
SWT는 Swing/SWT 통합을 위해 기본구조로서 SWT/AWT 브릿지(Bridge)를 제공한다. 브릿지는 RCP 어플리케이션에서 사용할 Swing 컴포넌트를 끼워넣는 방법으로 유일한 첫 단계이다. 이 기사의 많은 부분을 효과적인 통합에 필요한 추가적인 연습을 설명하는 것에 할예하고 있다. 이러한 연습이 없으면, SWT/AWT 브릿지는 사용하기에 충분하지 못하다. 연습을 함으로서, 여러분은 성공적으로 RCP 어플리케이션에서 Swing 컴포넌트를 통합할 수 있을 것이다.
두 가지의 독립된 UI 툴킷을 통합하는 것은 복잡한 작업이다, 그리고 그것은 거의 완벽한 결과를 생산하기 어렵다. 이 기사는 실용적인 기술(몇몇의 해킹)에 집중하여 실제 어플리케이션에 충분히 효과적으로 통합하도록 하고있다.
복잡성에도 불구하고, 아래에 묘사된 대부분의 사례들은 Swing 컴포넌트를 끼워넣을때 사용될 유일 공통 클래스로 캡슐화 될 수 있다. 구현을 지원하기위한 부가적인 헬퍼 클래스 들이 존재한다. 예제는 이 기사에 포함되어 있다. 부록 A, 예제 코드를 보면 더 많은 정보가 있다.
Using the SWT/AWT Bridge
SWT/AWT 브릿지의 사용
SWT/AWT 브릿지는 SWT 버전 3.0 이래로 포함되었다. 매우 단순한 API이고, org.eclipse.swt.awt 패키지에 위치해 있다.
- 이 기사는 AWT 프레임을 SWT 컴포지트로 끼워넣기 하는데 초점을 맞추고 있다. 그것은 단지 SWT/AWT 브릿지의 절반만을 보여준다. 그럼에도 아래에 설명한 이용의 대부분은 다른 방향을 적용하고 있다 : SWT 컴포지트를 AWT 프레임으로 끼워넣는 것으로.
최소한으로, AWT 프레임을 SWT Composite로 끼워넣는 것은 단지 아래의 간단한 두 라인이면 된다.
Composite composite = new Composite(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND); Frame frame = SWT_AWT.new_Frame(composite);
org.eclipse.swt.Composite의 인스턴스가 SWT.EMBEDDED 스타일로 생성되었다. 이 스타일은 AWT Frame이 Composite 내부로 끼워 넣어진다는 의미이다. SWT_AWT의 static new_Frame 메소드를 호출하는 것은 해당 frame을 생성하고 리턴하는 것이다. 그 frame은 AWT 혹은 Swing 컴포넌트들로 채워질 수 있을 것이다.
리턴된 프레임은 표준 AWT 프레임이 아니다. 이것은 java.awt.Frame의 서브 클래스로 네이티브 어플리케이션으로 끼워넣어진다는 것을 의미한다.
사실, 이것은 브라우저 창 내부에 끼워 넣어진 applet에 사용된 것과 같은 frame이다.
위에 보여진 예제의 코드는 매우 단순한다. SWT가 내부에서 두가지의 툴킷에 대해 통합을 많이 관리하는 반면, 브릿지의 구현의 범위는 매우 협소하다. 실제로, 여러분이 더욱 일관된 통합을 하려면 여러분의 어플리케이션에서 여러가지 작업을 해야한다. 이러한 추가적인 단계는 “Building on the SWT/AWT Bridge” 섹션 아래에 설명되어 있다.
Platform Considerations
플랫폼 고려사항들
플랫폼과 플랫폼 사이의 차이점으로 인한 SWT/AWT 블릿지의 사용에 대한 버전제약(Version Constraints)이 존재한다.
Table 1. Minimum Versions
Platform | JRE Version | Eclipse Version |
---|---|---|
Windows | 1.4 | 3.0 |
Linux/GTK | 1.5 | 3.0 |
Mac OS X | 1.5.0 Release 4 | 3.2 |
- Mac OS X 또한 SWT 호환 라이브러리의 설치가 필요하다.
- 이 기사를 작성할때, SWT/AWT 브릿지를 사용 시 Java 6과 Linux/GTK 플랫폼에서 GTK Look And Feel에서 행(hang)이 유발되었다. 이클립스 버그 91157과 Sun 버그 6386791에 더 많은 정보가 존재한다.
Multiple Event Threads
다중 이벤트 쓰레드
Swing/SWT 통합은 중요한 쓰레딩의 의미를 가지고 있다. 각각의 UI 툴킷은 자신의 이벤트 큐(Queue)를 가지며, 각각의 이벤트 큐는 각각의 쓰레드에 의해 처리된다. 대부분의 SWT API들은 반드시 SWT 이벤트 쓰레드로 부터 호출되어져야 한다. 엄격하게 강제되지는 않지만 Swing도 유사한 제약을 가지고 있다. 이 차이점이 툴킷을 합치는데 주요한 약점이며 몇몇의 코드의 복잡성을 추가한다.
어플리케이션은 반드시 현재의 쓰레드를 인지해야하고, 필요하다면, 작업을 적합한 UI 툴킷 쓰레드에서 수행하도록 스케줄 해야한다.
AWT 이벤트 쓰레드에서 작업을 스케줄하려면 아래의 방법을 사용한다.
javax.swing.SwingUtilities.invokeLater()
javax.swing.SwingUtilities.invokeAndWait()
SWT 이벤트 쓰레드에서 작업을 스케줄하려면 아래의 방법을 사용한다.
org.eclipse.swt.widgets.Display.asyncExec()
org.eclipse.swt.widgets.Display.syncExec()
위의 것들은 단일한 툴킷 환경에서 길게 작업하는 워커쓰레드(Worker Thread)에서 내려오는 동안 UI가 빠르게 반응하도록하는 같은 방법의 API이다. Swing/SWT 통합에서, 위의 것들은 하나의 쓰레드에서 다른 쓰레드로 작업을 이동시키는 목적으로 사용된다.
다중 이벤트 쓰레드 사용의 증가는 데드락(DeadLock)의 위험을 증가시킨다. 가급적 사용할때마다, 이벤트 쓰레드가 작업을 스케줄하고 있는 동안 다른 이벤트 쓰레드가 블록킹(Blocking)되는 것을 피하도록 한다. 다른 말로하면, 가급적 SWT 이벤트 쓰레드에서 SwingUtilities.invokeAndWait 을 호출하는 것을 피하고, AWT 이벤트 쓰레드에서 Display.syncExec 을 호출하는 것을 피하는 것이다. 그렇지 않으면, 하나의 블락킹(Blocking)된 호출이 다른 쓰레드가 그 자신을 블락킹하는 동안에 다른방향으로 블락킹 되서, 결국엔 데드락이 발생 할수 있을 것이다.
Building on the SWT/AWT Bridge
SWT/AWT 블릿지 기반으로 하기
만일 여러분이 SWT/AWT 브릿지 API를 단독으로 사용한다면, Swing 컴포넌트들은 여러분의 어플리케이션으로 신통치 않게 통합될 것이다. 이어지는 섹션은 통합에 있어서의 디테일한 문제들과 해결책을 제안한다. 모든 해결책들은 Swing/SWT 통합 관리를 돕기위한 추가적인 코드를 포함한다. 좋은 소식은 이 코드는 단독 사용자 끼워넣기 컴포지트 위젯(Single Custom Embedded Composite Wedget)으로 (몇몇의 지원 클래스들로) 캡슐와 될수 있고, 그것은 나머지 어플리케이션 코드와 분리 할 수 있다는 것이다.
Configuring the Swing Look and Feel
Swing 룩앤필 설정
아래에 SWT/AWT 브릿지의 최소 사용의 결과로서의 몇몇의 매우 기본적인 시각적(Visual) 문제를 보여주는 예가 있다.
아래에 SWT/AWT 브릿지의 최소 사용의 결과로서의 몇몇의 매우 기본적인 시각적(Visual) 문제를 보여주는 예가 있다.
Figure 3. Minimal Embedding of a Swing Component

위 그림에 약간의 즉각적이고 명확한 문제가 존재한다. Swing JTable(우상단)의 헤더와 스크롤바가 SWT-기반의 Error Log 뷰와 다르게 보인다.
이 예에서, JTable은 표준 스윙 Cross-Platform(Metal) 룩앤필로 전시되어 있다. 반면에, SWT 컴포넌트들은 네이티브 룩앤필(LookAndFeel)로 된 아래의 네이티브 위젯을 사용한다. Swing의 룩앤필이 반드시 네이티브 플렛폼에 맞게 바뀌어야한다. SWT/AWT 브릿지 자체로는 자동으로 네이티브 룩앤필로 설정되지 않기 때문에, 여러분 스스로 그렇게 해줘야 한다. 예를 들어
import javax.swing.UIManager; ... UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); ...어떤 AWT 혹은 Swing 컴포넌트가 생성되기전에, 위가 한번 수행되도록 하라.
![]() UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel"); |
Creating a Root Pane Container
루트팬 컨테이너 생성하기
SWT_AWT.new_Frame() 메소드는 SWT Composite 안에 끼워넣어지는 Java.awt.Frame의 서브 클래스 인스턴스를 리턴한다. Swing에서 임베디드 Frame을 사용할때 반드시 따라야하는 확실한 규칙이 존재한다.
SWT_AWT.new_Frame() 메소드는 SWT Composite 안에 끼워넣어지는 Java.awt.Frame의 서브 클래스 인스턴스를 리턴한다. Swing에서 임베디드 Frame을 사용할때 반드시 따라야하는 확실한 규칙이 존재한다.
- 임베디드 프레임의 첫번째 자식 컴포넌트는 반드시 중량(Heavyweight) 컨포넌트 여야한다. 그 컴포넌트는 반드시 전체 프레임에 채워져야 한다. 이 중량 컴포넌트는 정확하게 마우스 위치를 가져야하고 상호작용을 수용해야 한다.
- Swing으로 그 프레임을 보이도록 하려면, 여러분은 또한 반드시 root pane 컨테이너(javax.swing.RootPaneContainer)를 생성해야 한다. root pane은 모든 Swing 윈도우들의 기초이다. 그것은 모든 Swing 컴포넌트 들이 종속되는 계층화 기능을 제공한다.
- Swing의 구현체를 가정하기 때문에, root pane 컨테이너는 반드시 JFrame, JDialog, JWindow, 혹은 JApplet의 인스턴스 이여야 한다. 이러한 선택사항 때문에, JApplet이 적절한 선택이다. 왜냐하면 그것이 끼워넣어 질 수 있는 유일한 것이기 때문이다.
위의 각 규칙을 만족하기 위해선, JApplet을 임베디드 프레임의 유일한 자식으로 생성한다. JApplet을 사용하는 것은 에플릿 라이프사이클을 사용하는 진짜 애플릿을 생성하는 것을 의미하지 않는다. 여러분은 단지 브라우저에서 보여지는 것과 같은 디스플레이 컨테이너를 사용하는 것이다.
Composite composite = new Composite(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND); Frame frame = SWT_AWT.new_Frame(composite); JApplet applet = new JApplet(); frame.add(applet);
![[Note]](https://www.eclipse.org/articles/Article-Swing-SWT-Integration/images/note.gif)
Reducing Flicker
깜박임 줄이기추가적인 조치없이, SWT/AWT 브릿지를 사용하는것은 어플리케이션이 리사이즈되는 동안 임베디드 프레임에 과도한 깜박임을 유발하게 된다. 이유는 아래와 같다.
- 중량 컴포넌트(우리의 경우 javax.swing.JApplet)는 계층구조에서 현재적이다. 기본적으로, AWT 구현물은 모든 리사이즈 이벤트에서 컴포넌트의 배경(Background)를 클리어 한다.
- 임베디드 프레임에 의해 보다 더 많은 리사이즈 이벤트가 핸들링 될때, 단독 Swing 어플리케이션에서 기본 행동과 비교가 이루어진다. 이 증가(리사이즈 이벤트의 증가)는 임베디드 프레임을 둘러싸고 있는 SWT 컴포지트(Composite)가 AWT 프레임으로 지나가는 리사이즈 이벤트의 통로이기 때문에 그러하다.
Windows 환경에서, 속성값을 sun.awt.noerasebackground로 설정하면 깜박임이 줄어든다. 이 미표기 속성은 AWT 구현체에서 반복적인 배경 클리어를 불가하도록 한다. 이것은 반드시 어떠한 AWT 혹은 Swing 컴포넌트가 객체화 되기전에 설정되어야 한다.
System.setProperty("sun.awt.noerasebackground", "true");이 설정이 필요함에도, sun.awt.noerasebackground를 설정하는 것은 부정적인 결과를 가져온다. 배경을 클리어되지 않은체 놔두는 것은, 리사이즈와 Swing 컨텐츠 초기화 이전에 보여지는 동안, 이전에 그려진 픽셀(때때로 치즈(cheese)라고 불리우는)이 일시적으로 보여지는 결과를 가져올 수 있다.
이러한 효과는 부모 SWT 컴포지트에 리사이즈 리스너를 추가함으로서 제거될 수 있다. 컴포지트가 크게 리사이즈 되어, 그려지지 않는 영역이 노출되었을때 리스너는 측시 배경색으로 그 영역을 채워주게 된다. 그러면 치즈(cheese)는 즉시 제거되고, 임베디드 Swing 컴포넌트가 다시 그려질때 어떤 지연없이 꾸며져 향상된 화면을 제공한다.
class CleanResizeListener extends ControlAdapter { private Rectangle oldRect = null; public void controlResized(ControlEvent e) { // Prevent garbage from Swing lags during resize. Fill exposed areas // with background color. Composite composite = (Composite)e.widget; Rectangle newRect = composite.getClientArea(); if (oldRect != null) { int heightDelta = newRect.height - oldRect.height; int widthDelta = newRect.width - oldRect.width; if ((heightDelta > 0) || (widthDelta > 0)) { GC gc = new GC(composite); try { gc.fillRectangle(newRect.x, oldRect.height, newRect.width, heightDelta); gc.fillRectangle(oldRect.width, newRect.y, widthDelta, newRect.height); } finally { gc.dispose(); } } } oldRect = newRect; } }
Tab Traversal
탭 순회또 다른 Swing/SWT 통합의 고려는 각 툴킷에서 툴킷으로 탭(Tab)키가 순회할 때의 고려사항이다. 이 문제를 해결하려면 SWT/AWT 브릿지의 AWT 쪽에서 추가적인 작업이 필요하다. 적합한 탭 순회를 달성하려면, 임베디드 AWT 프레임을 위한 java.awt.FocusTraversalPolicy의 서브 사용자 클래스를 구현하라. 프레임 안에서 탭핑(tabbing)할때 사용자 정책이 표준정책을 위임하게 된다. 하지만 그것은 반드시 SWT 컴포넌트로 컨트롤을 다음과 같을때 이동한다.
- 프레임의 마지막 컴포넌트에서 탭핑 할때
- 프레임의 처음 컴포넌트에서 백-탭핑(back-tabbing) 할때
사용자 정책에서 앞으로 뒤로 순회하는 것을 구현하는 것은 간단하다. 하지만, 적절하게 getDefaultComponent 메소드를 구현하는 것은 까다롭다. 그것은 임베디드 AWT 프레임이 포커스를 가지지 않았을때 반드시 null을 리턴해야만 한다. 이 행동은 AWT가 어떻게 메소드를 콜(Call)하는 것에 대해서 핵킹한 것에 기반을 두고있고, 그것은 SWT의 순회 후에 AWT로 즉각적으로 포커스가 이동하는 것을 피하기 위해 필요하다. 더 많은 정보를 원하면, 부록의 EmbeddedChildFocusTraversalPolicy 클래스의 코맨트를 참조하라.
포커스 순회에 대해 더많은 정보를 위해선 AWT Focus Subsystem이라는 Swing 도큐먼트를 보라.
Modal Dialogs
모달 다이얼로그모달 다이얼로그가 Swing 컴포넌트에서 오픈되었을때, 전체 어플리케이션이 모달이 되어야 한다. SWT와 AWT는 각각의 이벤트 쓰레드를 가지고 있기 때문에, 그러한 다이얼로그는 기본적으로 모달이 아니다. SWT 이벤트 쓰레드는 Swing 모달 다이얼로그가 보여지는 동안
명시적으로 동작하지 않아야 한다. 이러한 효과는 Swing 다이얼로그가 보이는 동안 SWT 모달 다이얼로그를 오픈하는 것으로 쉽게 가능하다.
java.awt.Dialog awtDialog = ... Shell shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.NO_TRIM); shell.setSize(0, 0); shell.addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { awtDialog.requestFocus(); awtDialog.toFront(); } });가로세로 0 x 0의 크기는 Shell이 보여지는 것을 방지한다. 포커스 리스너는 어떻게든 SWT 윈도우가 포커스를 얻는다면, Swing 다이얼로그로 복구 하도록 할 것이다.(일예로, SWT 윈도우는 몇몇의 윈도우 메니저를 통해 여러분의 어플리케이션을 조종할때나 포커스를 얻을것이다.)
![[Note]](https://www.eclipse.org/articles/Article-Swing-SWT-Integration/images/note.gif)
shell.moveBelow(null); |
대안적으로, 모든 AWT 윈도우 이벤트를 설정하는 것으로 보다 명확하게 정확한 모달을 강제할 수 있다. Swing 모달 다이얼로그가 오픈되거나 보여지는 것이 탐지될 때마다, SWT 모달 윈도우가 오픈되어야 한다. 리스너의 예는 아래와 같고, 예제 코드에 완전히 구현되어 있다. 부록 A Example Code 에 더 많은 정보가 존재한다.
class AwtDialogListener implements AWTEventListener, ComponentListener { private final Display display; AwtDialogListener(Display display) { this.display = display; Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.WINDOW_EVENT_MASK); } private void handleRemovedDialog(Dialog awtDialog, boolean removeListener) { // An AWT dialog has been closed or set invisible. Close the SWT dialog ... } private void handleAddedDialog(final Dialog awtDialog) { // An AWT dialog has been opened or set visible. Open the SWT dialog // and add this as a component listener on the dialog to catch // visibility changes ... } private void handleOpenedWindow(WindowEvent event) { Window window = event.getWindow(); if (window instanceof Dialog) { handleAddedDialog((Dialog)window); } } private void handleClosedWindow(WindowEvent event) { // Dispose-based close Window window = event.getWindow(); if (window instanceof Dialog) { // Remove dialog and component listener handleRemovedDialog((Dialog)window, true); } } private void handleClosingWindow(WindowEvent event) { // System-based close // (see example code for full implementation) ... } public void eventDispatched(AWTEvent event) { switch (event.getID()) { case WindowEvent.WINDOW_OPENED: handleOpenedWindow((WindowEvent)event); break; case WindowEvent.WINDOW_CLOSED: handleClosedWindow((WindowEvent)event); break; case WindowEvent.WINDOW_CLOSING: handleClosingWindow((WindowEvent)event); break; default: break; } } public void componentHidden(ComponentEvent e) { Object obj = e.getSource(); if (obj instanceof Dialog) { // Remove dialog but keep listener in place so that we know if/when it is set visible handleRemovedDialog((Dialog)obj, false); } } public void componentShown(ComponentEvent e) { Object obj = e.getSource(); if (obj instanceof Dialog) { handleAddedDialog((Dialog)obj); } } ... }
Dismissing Pop-up Menus
팝업 메뉴 해체하기컨텍스트 메뉴가 임베디드 AWT 프레임에 전시될때, 프레임의 바깥쪽을 클릭할때 메뉴가 사라지지 않는 것을 볼 수 있다. 이러한 AWT의 제약사항은 잘 알려진 것이다, 하지만 같은 SWT 쉘에서 다중의 팝업이 보여지는 것은 임베디드 프레임에서 눈에 띄게 보기 좋지 않다.
Figure 4. Double Popups

이 문제에 대한 부분적인 해결방법이 존재한다.
- 사용자가 다른 윈도우에서 작업할때 수동으로 Swing 팝업을 해체한다. 임베디드 프레임에 윈도우 포커스 이벤트 리스너를 설정한다. 포커스를 잃어버리는 이벤트 발생시, 프레임의 윈도우와 컴포넌트 계층구조에서 javax.swing.JPopupMenu의 인스턴스를 찾을 수 있을 것이다.
* 이 해결책은 JRE 1.4나 그 이전버전에 적용된다; JRE 1.5나 그 이상의 버전에서는 필요없다. - 사용자가 SWT 메뉴에서 작업할때 수동으로 Swing 팝업을 해체한다. SWT 메뉴가 보여질때 마다 생성되는 SWT.Show 이벤트를 대기하는 디스플레이 필터를 설정한다. 이 필터의 이벤트 핸들러에서, 위에서 설명한것 처럼 보이는 Swing 팝업을 해체한다.
이러한 해결책에도 불구하고, 몇가지 이슈들이 남겨져 있다; 예를들어 워크벤치 윈도우 타이틀바에서의 사용자작업(상호작용)에서 Swing쪽 팝업이 해체되지 않는다.
Synchronizing with System Settings
시스템 설정과의 동기화사용자가 윈도우 컨트롤 패널을 통해 폰트 설정을 변경했을때, 그 변경 내용이 항상 적합하게 Swing 컴포넌트로 전파되지 않는다. 이 문제는 SWT 컴포넌트가 실제로 이러한 변화를 인식할때 SWT와 Swing 컴포넌트를 통합에서 특히 보여진다. 이러한 문제를 해결하려면, Swing 윈도우 룩앤필에서 시스템 폰트가 변경 될때 수동으로 폰트를 변경해야 한다.
다행이, Eclipse 3.2에서, 어플리케이션이 시스템 설정을 변경할때 새로운 SWT 이벤트가 추가 되었다. 그래서, Swing 폰트 변경은 아래와 같이 할수 있다.
Display display = ... Listener settingsListener = new Listener() { public void handleEvent(Event event) { handleSettingsChange(); } }; display.addListener(SWT.Settings, settingsListener);Swing 컴포넌트의 폰트 변경은 룩앤필을 통해 다루는것이 개별 컴포넌트의 폰트를 업데이트 시켜주는 것보다 최선의 방법이다.
private void handleSettingsChange() { ... org.eclipse.swt.graphics.Font currentSystemFont = ...; FontData fontData = currentSystemFont.getFontData()[0]; int resolution = Toolkit.getDefaultToolkit().getScreenResolution(); int awtFontSize = (int)Math.round((double)fontData.getHeight() * resolution / 72.0); // The style constants for SWT and AWT map exactly, and since they are int constants, they should // never change. So, the SWT style is passed through as the AWT style. java.awt.Font awtFont = new java.awt.Font(fontData.getName(), fontData.getStyle(), awtFontSize); FontUIResource fontResource = new FontUIResource(awtFont); UIManager.put("Button.font", fontResource); UIManager.put("CheckBox.font", fontResource); UIManager.put("ComboBox.font", fontResource); ... // many more similar calls Container contentPane = ... // content pane from the root pane SwingUtilities.updateComponentTreeUI(contentPane); } }첫째로, SWT 폰트를 동등한 AWT 폰트로 변환한다. AWT 폰트 사이즈는 항상 72dpi 해상도라고 상정한다. 실제 화면 해상도는 플랫폼 폰트 사이즈에서 AWT 포인트 사이즈로 화면에 보여질때 변환되어 맞춰져야 한다. 그런다음, 다양한 Swing 컴포넌트 타입의 폰트는 룩앤필의 javax.swing.UImanager를 통해 변경된다.
Keystroke Contention
키스트로크 경쟁만일 여러분이 RCP 어플리케이션의 글로벌 액션과 임베디드 Swing 컴포넌트의 핸들러에 같은 키스트로크를 맵핑했다면, 예상치 못한 결과가 있을 수 있다. org.eclipse.ui.bindings를 통해 정의된 키스트로크의 확장 포인트는 Swing 컴포넌트가 포커스 되었을지라도, Swing 컴포넌트에 정의된 것보다 상위에 있을 것이다. 이 경우에, RCP 바인딩은 디스플레이 필터를 통해 관리되고, 그래서 Swing 컴포넌트에 도달하기 전에 소모될 것이다. 결과적으로, 임베디드 컴포지트 레벨에서 이 문제를 피할 수 있는 단순하고, 일반적인 해결방법은 존재하지 않는다. 대신 다음과 같은 방법으로 충돌을 피할 수 있다.
- Swing 컴포넌트에 포커스가 있을때 org.eclipse.ui.contexts 확장 포인트가 키 바인딩을 조직하는 동안 보여지지 않도록 한다.
- RCP 액션에서 Swing 컴포넌트의 액션을 복제하여, 같은 키스트로크로 결합시킨다. RCP 액션으로의 결합(Binding)은 org.eclipse,ui.contexts 확장을 통해 끼워넣어진다. 액션은 아래에 있는 Swing 액션을 호출하는 것으로 구현되어질수 있다.
public void keyReleased(KeyEvent e) { if (e.keyCode == SWT.F10 && (e.stateMask & SWT.SHIFT) != 0) { e.doit = false; } }
Other Workarounds
그외의 해결책들
버그 6411042에 기인하여, SWT이벤트 쓰레드로 부터 AWT/Swing 컴포넌트들이 추가되어질때, 메모리 릭(leak)이 발생한다. 이것을 피하려면, AWT 이벤트 쓰레드로 이러한 컴포넌트들을 생성하고 추가하면 된다. 단지 javax.swing.SwingUtilities.invokeLater()을 통해 생성코드를 호출하면 된다. 이전에 논의한것 처럼, AWT 이벤트 쓰레드로부터 Swing API를 호출하는 것은 일반적이고 좋은 습관이다.Unresolved Issues
미해결 이슈들ClearType
클리어타입윈도우 JRE 1.5와 이전버전에서, Swing 시스템 룩앤필은 완벽하게 클리어타입 안티알리아싱(Anti-Aliasing)을 지원하지 않는다. 기본적으로, 클리어타입이 활성화되고 같은 폰트와 사이즈가 Swing과 SWT 컨트롤들에 선택 되었을때, 폰트는 다르게 보여진다. 부분적인 해결책은 swing.aatext 속성을 "true"로 설정하는 것으로 가능하다. 이런 미표기 속성은 Java6 이전의 환경에서 폰트 안티알리아싱을 가능하게 하지만, 그것은 단지 안티알리아싱을 켜기만 한다. 그것은 현재의 윈도우즈 시스템 셋팅값들과 동기화되지는 않는다. Swing은 자신만의 안티알리아싱 알고리즘을 가지고 있지만, 네이티브 디스플레이 폰트와 미세한 차이가 보여진다. 이 문제는 Java6에서 해결되었다. 아래의 스크린샷은 이러한 문제를 보여준다. 여기에 두개의 SWT 기반의 뷰가 왼쪽에 있고, Swing 테이블 컴포넌트가 오른쪽에 있다.
Figure 5. Font Differences with ClearType

Cursor Synchronization
커서 동기화
SWT 커서의 변화는 임베디드 Swing 컴포넌트에 반영되지 않는다. SWT 커서 변화는 마우스가 임베디드 프레임 위에 있을때 디폴트 화살표 커서로 되돌아 간다.
Conclusion
결론
Swing 컴포넌트의 RCP 어플리케이션으로의 부드러운 통합은 가능하다. 하지만 그것은 SWT/AWT 브릿지 이상의 것을 요구한다. 여러분은 위에 설명한 기교를 모듈방식으로 실제 어플리케이션을 위한 SWT/AWT 브릿지의 행동을 개선시키는 것을 구현할 수 있다.
Appendix A, Example Code부록 A, 예제코드는 이러한 모든 추가적인 연습을 보여주는 예제 코드를 포함하고 있다.
댓글
댓글 쓰기