2013년 12월 25일 수요일

SWT/JFace 이벤트 처리




SWT에서의 이벤트 처리






 SWT 이벤트 처리 주기



  SWT 이벤트 처리는 운영체제의 이벤트 큐에서 시작됩니다. 이벤트 큐는 사용자의 동작을 기록하고 목록을 관리하는 역할을 합니다. 

  SWT 애플리케이션을 실행하면 애플리케이션의 Display 클래스는 해당 큐의 내용을 정렬하는데, 이 때 readAndDispatch() 메서드와 msg 필드를 이용합니다. msg 필드는 운영체제의 메시지 큐에 대한 핸들로 동작합니다. 

  만약 관련된 어떤 것을 찾으면, 해당 이벤트를 최상위의 Shell 객체로 보내며, Shell 객체는 어떤 위젯이 해당 이벤트를 받을 지 결정합니다.

  Shell은 이벤트를 사용자가 작동한 위젯으로 보내고, 해당 위젯은 이 정보를 리스너로 불리는 인터페이스로 전달합니다.

  리스너 메서드 중에 하나가 필요한 절차를 수행하거나, 사용자 동작을 처리하는 또 다른 메서드인 이벤트 핸들러를 호출합니다.



  이벤트에 응답하는 위젯을 만들 때, GUI 설계자의 주요 작업어떤 이벤트를 작동해야 하는지를 결정하는 것이러한 이벤트를 감지하는 리스너를 생성하고 결부시키는 것, 그리고 적절한 처리를 할 수 있도록 이벤트 핸들러를 구축하는 작업이다.




유형 리스너와 이벤트 사용


  SWT에 있는 대부분의 리스너 인터페이스는 특정 사용자 동작에 대해서만 반응합니다. 이러한 리스너를 유형 리스너(typed listeners)라고 부르며 이 리스너들은 TypedListener 클래스를 상속 받습니다. 이와 유사하게, 특정한 동작으로 발생하는 이벤트를 유형 이벤트(typed event)라고 하는데 이들은 TypedEvent의 하위 클래스입니다. 

  예를 들어, 마우스 클릭이나 더블클릭은 MouseEvent로 표현되며, 적절한 MouseListener가 받아서 처리합니다. 사용자가 수행한 키보드 작동은 KeyEvent로 전환되며 KeyListener가 받아서 처리합니다. 



SWT Event 클래스와 관련 리스너



  기능 구현을 위해, 이러한 리스너를  GUI 컴포넌트와 연결해야 합니다. 예를 들어, TreeListener는 TreeEvent만 받아들이며, 이를 위해서는 Tree 객체와 연결되어 있어야 합니다. 하지만, 모든 GUI 컴포넌트들이 각각의 리스너를 사용하는 것은 아닙니다. 예를 들어, 위의 표의 GUI 컴포넌트 열에서처럼, Control 컴포넌트는 Tracker 객체 외에도 많은 이벤트 타입을 발생시킵니다. 또한 MenuListener 와 TreeListener 등과 같은 리스너는 매우 한정된 위젯에만 붙을 수 있습니다.

  리스너는 컴포넌트의 add..Listener() 메서드를 호출 할 때 유형 리스너를 인자로 넣음으로써 컴토넌트에 부착합니다.



  위 표의 이벤트 열은 TypedEvent의 하위 클래스를 보여주고 있습니다. TypedEvent는 Display와 Shell 객체가 유형 리스너에게 보내는 이벤트입니다. 프로그래머가 보통 이 클래스를 직접 조작하지는 않지만, 클래스는 이벤트 발생에 따른 정보를 제공하는 멤버 필드를 가집니다. 이벤트 핸들러가 환경에 대한 정보를 획득하기 위해 이 정보를 사용합니다.





모든 유형 이벤트에 있는 일반적인 데이터 필드



  대부분 이벤트 클래스가 사용자 동작에 대한 더 많은 정보를 제공하는 추가 필드를 갖습니다. 
예를 들어, MouseEvent 클래스는 button 필드를 포함하는데, button 필드는 어떤 마우스 버튼이 눌렸는지와 마우스가 동작한 위젯 기준 좌표의 x,y값이 무엇인지를 알려줍니다.

  ShellEvent 클래스는 doit 이라는 부울 필드를 갖는데, 주어진 동작에 대해 의도한 결과가 발생했는지를 이 필드에 명시할 수 있습니다.



  리스너를 사용하기 위해 컴포넌트의 add...Listener() 메서드를 이용하여 익명 리스너 인터페이스를 만듭니다. 이 메서드는 리스너의 범위를 컴포넌트에 한정시키게 합니다.


아래의 예제는 버튼을 생성하여 버튼에 이벤트를 연동한 예제입니다. 

<예제 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.swtjface.ch4;
  
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
 
/**
 * 2010.05.28
 * 버튼 이벤트 리스너 테스트 
 * @author cremazer
 */
public class WidgetWindow {
 public static void main(String[] args) {
    
  //할당과 초기화
  Display display = new Display();
  Shell shell = new Shell(display);
 
  //쉘에 위젯 추가하기
  Button bt = new Button(shell, SWT.PUSH | SWT.CENTER);
  bt.setText("Button");
  bt.setSize(220, 60);
  bt.addMouseListener(new MouseListener() {
   
   @Override
   public void mouseUp(MouseEvent arg0) {
    clkupEventHandler();
   }
   
   @Override
   public void mouseDown(MouseEvent arg0) {
    clkdwnEventHandler();
   }
   
   @Override
   public void mouseDoubleClick(MouseEvent arg0) {
    dblclkEventHandler();
   }
  });
  
  //GUI 작동
  shell.pack();
  shell.setText("Widget Window");
  shell.open();
  while(!shell.isDisposed()){
   if(!display.readAndDispatch()){
    display.sleep();    
   }
  }
  display.dispose();
 }
 
 static void dblclkEventHandler(){
  System.out.println("Double Click.");
 }
 
 static void clkdwnEventHandler(){
  System.out.println("Click - down.");
 }
 
 static void clkupEventHandler(){
  System.out.println("Click - up.");
 }
}



<결과>





  위의 예제 코드는 MouseListener 의 세가지 메서드를 선언했습니다. 하지만 만약 더블 클릭 이벤트에만 관심이 있어서, mouseDoubleClick() 으로만 작업하고 싶다면, 어댑터라는 클래스를 사용하므로써 불필요한 코드를 제거할 수 있습니다.




어댑터


  어댑터(Adapter)는 추상 클래스로서 리스너 인터페이스를 구현하고, 필요한 각 메서드의 기본적인 구현을 제공합니다. 위젯을 리스너가 아닌 어댑터와 연결한다면, 단지 관심있는 메서드의 코드만 작성하면 됩니다. 이 것은 실제로 복잡한 GUI를 작업하게 될 때 프로그래밍 시간을 상당히 줄여 줄 수 있습니다.





SWT 어댑터 클래스와 이에 상응하는 리스너 인터페이스


 다음은 마우스어댑터를 사용한 예제입니다.



<예제 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.swtjface.ch4;
 
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
 
/**
 * 2010.05.30
 * 버튼 마우스어댑터 테스트 
 * @author cremazer
 */
public class AdapterTest {
  public static void main(String[] args) {
    
  //할당과 초기화
  Display display = new Display();
  Shell shell = new Shell(display);
 
  //쉘에 위젯 추가하기
  Button bt = new Button(shell, SWT.PUSH | SWT.CENTER);
  bt.setText("Button");
  bt.setSize(220, 60);
  bt.addMouseListener(new MouseAdapter() {
    public void mouseDoubleClick(MouseEvent arg0) {
     dblclkEventHandler();
     }
  });
  
  //GUI 작동
  shell.pack();
  shell.setText("Widget Window");
  shell.open();
  while(!shell.isDisposed()){
   if(!display.readAndDispatch()){
    display.sleep();    
   }
  }
  display.dispose();
 }
 
 public static void dblclkEventHandler(){
  System.out.println("Double Click!");
 }
}



<결과>






키보드 이벤트

  
  키보드 이벤트는 KeyEvent 클래스를 포함하는데, 이는 키를 누를 때 생성되며, 하위 클래스인 TraverseEvent와 VerifyEvent를 갖습니다. TraverseEvent는 사용자가 화살표 키나 탭 키를 눌러 다음 위젯으로 포커스를 옮기려 할 때 일어납니다. VerifyEvent는 사용자가 텍스트를 넣고 다음 동작을 하기 전에, 입력한 텍스트를 검증하고자 할 때 발생합니다.



  상속받은 TypedEvent와  EventObject 클래스의 필드 외에 KeyEvent 클래스는 세 개의 멤버 필드를 가지고 이벤트를 일으키는 키와 관련된 정보를 제공합니다.
  

  • character : 누른 키를 나타내는 char 값을 제공합니다. 
  • stateMask : 키보드에서 기능키의 상태를 표현하는 정수를 반환합니다. 해당 정수를 조사하여 프로그램은 Alt, Ctrl,Shift, Command 키 등이 눌렸는지를 알아냅니다. 
  • keyCode : SWT public 상수에 해당하는 키 코드(key code)를 제공합니다.






다음 예제는 KeyEvent를 사용하는 예제입니다.



<예제 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.swtjface.ch4; 
 
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell; 
 
/**
 * 2010.05.31
 * 키 리스너 테스트 
 * @author cremazer
 */
public class KeyListenerTest {
 public KeyListenerTest(){
    
  //할당과 초기화
  Display display = new Display();
  Shell shell = new Shell(display); 
 
  //쉘에 위젯 추가하기
  Button bt = new Button(shell, SWT.CENTER);
  bt.setText("Button");
  bt.setSize(220, 60);
  bt.addKeyListener(new KeyAdapter() {
   public void keyPressed(KeyEvent e) {
    String str = "";
    if( (e.stateMask & SWT.ALT) != 0 ) str += "ALT-";
    if( (e.stateMask & SWT.CTRL) != 0 ) str += "CTRL-";
    if( (e.stateMask & SWT.COMMAND) != 0 ) str += "COMMAND-";
    if( (e.stateMask & SWT.SHIFT) != 0 ) str += "SHIFT";
    switch(e.keyCode){
    case SWT.BS :  str += "BACKSPACE" ; break;
    case SWT.CR :  str += "CARRIAGE RETURN" ; break;
    case SWT.DEL  : str += "DELETE" ; break;
    case SWT.ESC  : str += "ESCAPE" ; break;
    case SWT.LF :  str += "LINE FEED" ; break;
    case SWT.TAB : str += "TAB" ; break;
    default :    str += e.character; break;
    }
    System.out.println(str);
   }
  });
  
  //GUI 작동
  shell.pack();
  shell.setText("Widget Window");
  shell.open();
 
  while(!shell.isDisposed()){
   if(!display.readAndDispatch()){
    display.sleep();    
   }
  }
  display.dispose();
 }
 
 public static void main(String[] args) {
  KeyListenerTest klt = new KeyListenerTest();
 }
}


<결과>





  위 예제는 KeyEvent 필드와 public 상수를 사용하여 문자열을 생성하는데, 이 문자열은 누르는 키와 기능키의 이름을 표시합니다. 이벤트 핸들러 작업에서의 첫 번째 단계는 이벤트의 stateMask 필드를 검사하여 Alt, Ctrl, Shift, Command 키가 눌렸는지를 확인하는 것입니다. 그리고 기능키의 이름을 문자열에 덧붙입니다. 이 메서드는 계속하여 이벤트의 keyCode가 알파벳이나 숫자 혹은 기타 다른 키와 일치하는지 체크합니다.




키보드 내용과 SWT 키 코드 상수



  TraverseEvent는 사용자가 키를 누름으로써 버튼이나 체크 박스와 같은 하나의 컴포넌트에서 다른 컴포넌트로 포커스가 이동될 때 발생합니다. 이 클래스에 있는 두 개의 필드를 사용하여 포커스 이동 동작으로 인해 다른 컨트롤로 옮길 것인지, 아니면 이벤트를 발생시킨 위젯에 놔둘 것인지를 제어할 수 있습니다.


  • doit : 주어진 위젯에서 포커스 이동을 허락할지(TRUE) 허락하지 않을지(FALSE)를 결정하는 부울값입니다. 
  • detail : 이벤트를 일으킨 키를 정수로 표시합니다.  


VerifyEvent의 사용방법은 TraverseEvent와 유사하며, 목표는 사용자  동작이 기본 동작이나 일반적인 동작을 일으키는지 아닌지를 알아내는 것입니다.



무형 이벤트로 맞춤 이벤트 처리하기


  유형 이벤트와 리스너는 이벤트 처리를 클래스, 인터페이스와 함께 명백하게 작업 목적에 맞춥니다. 더욱이 유형 리스너는 특정한 메서드를 제공하여 이벤트를 받고 처리합니다. 유형 컴포넌트는 리스너와 이벤트의 범위를 좁혀 특정 작동만을 처리하도록 함으로써, 코딩 에러의 가능성을 줄입니다.

  하지만 만약 코딩의 유연성을 안정성보다 선호한다면, SWT가 제공하는 무형 이벤트와 리스너를 사용하면 됩니다.



<예제 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.swtjface.ch4; 
 
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell; 
 
/**
 * 2010.06.01
 * 무형 리스너 테스트 
 * @author cremazer
 */
public class UntypedListenerTest {
 public UntypedListenerTest(){
    
  //할당과 초기화
  Display display = new Display();
  Shell shell = new Shell(display); 
 
  //쉘에 위젯 추가하기
  Listener listener = new Listener(){
   @Override
   public void handleEvent(Event e) {
    switch(e.type){
     case SWT.KeyDown :
      if(e.character == 'b'){
       System.out.println("Key" + e.character);
      }
      break;
     case SWT.MouseDown :
      if(e.button == 3){
       System.out.println("Right Click");
      }
      break;
     case SWT.MouseDoubleClick :
      System.out.println("Double Click");
      break;
    }
   }
  };
  
  Button bt = new Button(shell, SWT.CENTER);
  bt.setText("Button");
  bt.setSize(220, 60);
  bt.addListener(SWT.KeyDown, listener);
  bt.addListener(SWT.MouseDown, listener);
  bt.addListener(SWT.MouseDoubleClick, listener);
  
  
  //GUI 작동
  shell.pack();
  shell.setText("Widget Window");
  shell.open();
  while(!shell.isDisposed()){
   if(!display.readAndDispatch()){
    display.sleep();    
   }
  }
  display.dispose();
 }
 
 public static void main(String[] args) {
  UntypedListenerTest ult = new UntypedListenerTest();
 }
}


<결과>




  위 예제에서 Listener 객체는 유일한 메서드인 handleEvent()로 모든 Event 인스턴스를 보냅니다. 이후, Event의 type 필드는 어떤 절차를 수행해야 하는지를 결정합니다. 만약 이벤트가 SWT.Keydown 유형이고 그 문자가 b라면 해당 문장을 콘솔에 보내며, SWT.MouseDown 유형이면서 세 번째 마우스 버튼이 눌리면(오른쪽 버튼을 클릭하면) Right Click이라는 문장을 보여줍니다. 혹은, SWT.MouseDoubleClick 이벤트가 발생한다면, Double Click이라는 문장을 보여줍니다.



  유형 리스너와 이벤트로 이러한 기능을 구현할 수도 있으나, 처리 과정이 좀더 복잡합니다. 버튼에 MouseListener와 KeyListener를 관련 어댑터와 함께 덧붙여야 하며, 이벤트 처리 루틴을 적절한 리스너 메서드에 작성해야 하기 때문입니다.

  유형 이벤트를 대신하기 위해서, Event 클래스는 유형 이벤트의 모든 필드를 포함합니다. Event 클래스는 KeyEvent로서 문자 필드와 MouseEvent로서 버튼 필드를 갖습니다. 또한 위의 코드에서 type이라는 필드를 가지고 이벤트의 속성을 참조합니다. 아래의 표는 type의 목록을 보여주고 있습니다.


Event 클래스의 SWT type 값



  이번 예제는 두 개의 버튼과 하나의 레이블 그리고 필요한 이벤트 처리를 하는 예제입니다.

  

<통합 예제 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.swtjface.ch4; 
 
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener; 
 
/**
 * 2010.06.01
 * 통합 예제
 * 두 개의 버튼과 하나의 레이블 및 이벤트 처리 테스트
 * @author cremazer
 *
 */
public class Ch4_MouseKey extends Composite {
    
 Label output;
 
 public Ch4_MouseKey(Composite parent) {
  super(parent, SWT.NONE);
  
  Button typed = new Button(this, SWT.PUSH);
  typed.setText("Typed");
  typed.setLocation(2,10);
  typed.pack();
  typed.addKeyListener(new KeyAdapter(){
   public void keyPressed(KeyEvent e){
    keyHandler();
   }
  });
  
  Button uptyped = new Button(this, SWT.PUSH);
  uptyped.setText("Uptyped");
  uptyped.setLocation(80,10);
  uptyped.pack();
  uptyped.addListener(SWT.MouseEnter, UntypedListener);
  uptyped.addListener(SWT.MouseExit, UntypedListener);
  
  output = new Label(this, SWT.SHADOW_OUT);
  output.setBounds(40, 70, 90, 40);
  output.setText("No Event");
  
  pack();
 }
 
 Listener UntypedListener = new Listener(){
  @Override
  public void handleEvent(Event e) {
   switch(e.type){
    case SWT.MouseEnter :
     output.setText("Mouse Enter");
     break;
    case SWT.MouseExit :
     output.setText("Mouse Exit");
     break;
   }
  }
 };
 
 void keyHandler(){
  output.setText("Key Event");
 }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.swtjface.ch4; 
 
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell; 
 
/**
 * 2010.06.01
 * 통합 SWT 리스너/이벤트 테스트 
 * @author cremazer
 */
public class Ch4_MouseKeyTest {
 public Ch4_MouseKeyTest(){
    
  //할당과 초기화
  Display display = new Display();
  Shell shell = new Shell(display); 
 
  //쉘에 위젯 추가하기
  Ch4_MouseKey mk = new Ch4_MouseKey(shell);
  
  //GUI 작동
  shell.pack();
  shell.setText("Widget Window");
  shell.open();
  while(!shell.isDisposed()){
   if(!display.readAndDispatch()){
    display.sleep();    
   }
  }
  display.dispose();
 }
 
 public static void main(String[] args) {
  Ch4_MouseKeyTest mkt = new Ch4_MouseKeyTest();
 }
}


<결과>




  첫 번째 버튼은 익명의 유형 리스너와 결합되었는데, 이 리스너는 선택된 키보드 이벤트를 받습니다.

  두 번째 버튼에는 UntypedListener 인터페이스를 추가했는데, 이는 마우스 포인터가 버튼에 들어올 때와 나갈 때 발생하는 이벤트를 잡아냅니다. 마우스 양쪽 버튼이 이벤트를 발생시킬 때마다 문자열을 레이블로 보냅니다.


  위 예제의 SWT 구조는 위젯이 다양한 형태의 이벤트를 받고, 여러 다른 응답을 하도록 해줍니다. 하지만 대부분의 GUI에서 이는 필요치 않습니다. 이러한 경우 SWT의 폭넓은 기능은 이벤트 처리 코드의 코딩을 복잡하게 할 뿐입니다. 따라서 이런 능력과 간편성을 맞바꾸려고 할 때 JFace 이벤트 모델이 매우 유용하다는 것을 알게 될 것입니다.





JFace의 이벤트 처리


  복잡한 사용자 인터페이스를 다룰 때, 이벤트 핸들링 기능을 이벤트를 생성하는 GUI 컴포넌트로부터 분리하는 것이 좋습니다. 이는 하나의 그룹이 GUI 처리에 대해 작업하고, 다른 그룹은 외관을 설계할 수 있도록 해줍니다. 또한 리스너의 기능을 어떤 컴포넌트에는 덧붙일 수 있고, 그 코드를 더욱 자주 재사용할 수 있게 합니다. 결국, 이 프로그램의 한 부분은 GUI의 뷰를 엄격히 다루고, 또 다른 하나는 이벤트 처리만 다룹니다. 이렇게 해야 코드를 개발하고 이해가기가 좀더 쉽습니다.



  JFace는 Action과 ActionContributionItem 클래스를 분리해 제공합니다. 간단하게 ActionContributionItem의 GUI 위젯의 기능과 그와 덧붙여진 리스너 클래스를 결합하여 사용합니다. 사용자가 상호작용을 할 때마다, 연관된 Action 클래스를 발생시켜, 이벤트를 처리합니다.



액션(Action)과 컨트리뷰션(Contribution) 이해하기


  JFace는 이벤트 처리를 좀 더 직관적으로 만들고, 간단한 몇 줄의 코드로써 이벤트를 받고 사용할 수 있게 하는데 그 목표가 있습니다. 해당 목표에 이르기 위해서 JFace는 세 가지 가정을 합니다. 


  • 사용자 입력은 버튼, 툴바, 메뉴를 포함합니다.
  • 각 컴포넌트는 단 하나의 연관 이벤트만을 갖습니다.
  • 각 이벤트는 단 하나의 이벤트 핸들러를 갖습니다.


  JFace는 이러한 가정을 취함으로써, 이벤트 처리를 상당히 간소화합니다.

  첫 번째 가정은 컨트리뷰션이 단지 세 가지 형태중에 하나만을 취해야 한다는 것을 의미합니다.

  두 번째 가정은 컨트리뷰션을 결합된 액션과 분리하는 것입니다. 만약 각 컨트리뷰션 컴포넌트가 단지 하나의 이벤트를 발생시킨다면, 어떤 액션이 필요한 것인지, 어떤 컴포넌트가 이벤트를 일으킨 것인지는 상관이 없습니다.

  세 번째 가정은 각 액션이 단 하나의 이벤트 핸들링 루틴을 필요로 한다는 것을 의미합니다.



SWT/JFace의 이벤트 모델



  인터페이스 처리는 Display 클래스로 시작하여 운영체제의 이벤트 큐를 모니터링 합니다. 하지만, Display 클래스가 Display의 Shell 객체를 포함하는 ApplicationWindow로 정보를 전달합니다. ApplicationWindow는 Action 클래스를 생성하여, 이를 원 이벤트를 생성한 컨트리뷰션으로 전달합니다. 컨트리뷰션은 단일 이벤트 핸들러로써 Action 클래스의 run() 메서드를 호출합니다.





SWT/JFace 모델에서 컨트리뷰션 능력을 가진 클래스와 인터페이스



  Action 클래스는 SWT Event 클래스와 유사하게 동작하지만, 컨트리뷰션 관련 기능은 좀 더 복잡합니다. 두 개의 주요 컨트리뷰션 관련 클래스는 ContributionItem 클래스와 ContributionManager 클래스입니다. ContributionItem 클래스는 액션을 발생시키는 개별적인 GUI 컴포넌트를 제공하고, ContiributionManager 클래스는 ContibutionItem들을 포함할 수 있는 객체를 제공합니다. 이들은 모두 추상클래스이므로, 이벤트 처리 기능은 그 하위 클래스에서 제공합니다.





Action 클래스 생성


  아래의 코드는 Action 클래스의 하위 클래스를 생성하는 코드이며, 이 클래스는 문자열을 ApplicationWindow의 상태표시줄에 출력하는 기능을 수행합니다.

  툴바 안에 이 클래스를 구현할 것이므로, 연결시킬 이미지가 필요합니다. 이미지는 $ECLIPSE_HOME/plugins/org.eclipse.platform_x.y.z 디렉토리에서 eclipse.gif를 현재 프로젝트 폴더에 복사하는 것입니다.






<생성 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.swtjface.ch4;
 
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.resource.ImageDescriptor;
 
/**
 * 2010.06.02
 * Action 클래스 생성 
 * @author cremazer
 */
public class Ch4_StatusAction extends Action {
    
 StatusLineManager statman;
 short triggercount = 0; 
 
 //생성자
 public Ch4_StatusAction(StatusLineManager sm){
  super("&Trigger@Ctrl+T", AS_PUSH_BUTTON);
  statman = sm;
  setToolTipText("Trigger the Action"); 
  setImageDescriptor(ImageDescriptor.createFromFile(this.getClass(), "eclipse.gif"));
 }
 
 public void run(){
  triggercount++;
  statman.setMessage("The status action has fired. Count : " + triggercount);
 }
}



  run() 메서드는 이벤트 처리를 하지만, 이 클래스의 주요 작업은 생성자가 수행합니다.

  첫 째, 생성자는 상위 클래스인 Action의 생성자를 호출하고, TEXT와 STYLE 필드를 초기화합니다. 만약 Ch4_StatusAction을 메뉴에 통합시키면, 메뉴 항목인 레이블은 이벤트를 발생시킬 수 있게 됩니다. 이 때 T 앞에 있는 &는 이 문자가 단축키(accelerator key)가 될 것이라는 것을 의미합니다. TEXT 필드의 Ctrl+T는 사용자가 Ctrl과 T 키를 동시에 눌렀을 때 이 액션이 발생한다는 것을 의미합니다.





ApplicationWindow에서 컨트리뷰션 구현하기


  아래 예제코드는 어떻게 ContributionItem과 ContributionManager 클래스를 윈도우에 추가하는지 보여줍니다.



<에제 코드>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.swtjface.ch4;
 
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display; 
 
/**
 * 2010.06.02
 * 액션 컨트리뷰션 예제 
 * @author cremazer
 */
public class Ch4_Contributions extends ApplicationWindow {
    
 StatusLineManager slm = new StatusLineManager();
 Ch4_StatusAction statusAction = new Ch4_StatusAction(slm);
 
 //statusAction 컨트리뷰션을 할당한다.
 ActionContributionItem aci = new ActionContributionItem(statusAction); 
 
 //Application Window에 자원을 추가한다.
 public Ch4_Contributions() {
  super(null); 
  addStatusLine();  //상태표시줄 추가
  addMenuBar();  //메뉴 추가
  addToolBar(SWT.FLAT | SWT.WRAP);  //툴바 추가
 }
 
 protected Control createContents(Composite parent){
  getShell().setText("Action/Contribution Example");
  parent.setSize(290,150);
  aci.fill(parent); //윈도우 안에 버튼을 생성한다.
  return parent;
 }
 
 public static void main(String[] args) {
  Ch4_Contributions swin = new Ch4_Contributions();
  swin.setBlockOnOpen(true);
  swin.open();
  Display.getCurrent().dispose();
 }
 
 protected MenuManager createMenuManager(){
  MenuManager mainMenu = new MenuManager(null);
  MenuManager actionMenu = new MenuManager("Menu");
  mainMenu.add(actionMenu);  
  actionMenu.add(statusAction); //statusAction 컨트리뷰션을 할당한다.
  
  return mainMenu;
 }
 
 protected ToolBarManager createToolBarManager(int style){
  ToolBarManager toolBarManager = new ToolBarManager(style);
  toolBarManager.add(statusAction); //statusAction 컨트리뷰션을 할당한다.
  
  return toolBarManager;
 }
 
 protected StatusLineManager createStatusLineManager(){
  return slm;
 }
}



 <결과>




  결과 화면에서 아래의 상태표시줄은 Ch4_StatusAction을 실행한 횟수를 기록합니다.





컨트리뷰션과 연결하기

  
  GUI에 ActionContributionitem을 통합하는 주요 방법은 두 가지가 있습니다.

  첫 번째는 ContributionManager의 하위 클래스의 add() 메서드를 사용하는데, 이는 Ch4_Contributions 애플리케이션에서 MenuManager와 ToolBarManager가 수행합니다.

  두 번째는 ActionContributionItem와 관련되어 있는 fill() 메서드를 사용하고, 해당 메서드 인자로 SWT 위젯을 넘기는 것입니다.


ActionContributionItem의 fill() 메서드와 관련된 외양



참고서적 : SWT/JFace in Action



댓글 없음:

댓글 쓰기