Eclipse 4 RCP 정리
1. Eclipse e4 install
https://download.eclipse.org/e4/snapshots/org.eclipse.e4.tools/latest/
Help – Install new software를 통해 위의 URL을 입력하고 tool들을 모두 체크한 다음 설치를 해주면 됩니다.
이클립스 최신 버전을 설치했을 경우 e4가 자동적으로 설치가 되어있을 수 있지만, Spy와 관련된 기능들은 설치가 되어 있지 않을 수가 있어 설치를 진행해줍니다.(현재 이클립스 버전 2020-06 (4.16.0))
2. Create Project
기존 RCP와 유사하게 Plug-in Project로 프로젝트를 생성합니다.
이클립스 버전은 3.5이상으로 설정해주고,
이용할 플러그인 템플릿을 Eclipse 4 RCP Application을 선택하면 됩니다.
마지막으로 create sample content에 체크를 해주는데, 기본적인 part와 model, menu, command, handler 등 RCP 4에서 지원하는 새로운 기능들을 확인하기 위해서 체크를 해주는 것이 좋습니다. 그리고 finish를 해주면
이와 같은 프로젝트에 파일들이 자동적으로 생성이 됩니다.
3. Parts
파트(Part)들은 사용자 인터페이스 컴포넌트로서 우리는 파트를 통해서 데이터를 수정하거나 돌아다닐 수 있다. 파트들은 스택처럼 서로 겹쳐 쌓일수도 있고, 컨테이너에 담길수 도 있으며, 이 컨테이너에 의해서 서로 옆으로 나란히 위치시킬 수도 있다. 하나의 파트는 드랍다운 메뉴와 컨텍스메뉴 그리고 툴바를 가질 수 도 있다.
예에서 사용된 파트는 Sample Part고, e4xmi의 Application – Windows – Controls에서 Part를 정의할 수 있다.
package gef5test.parts;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.e4.ui.di.Persist;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
public class SamplePart {
private TableViewer tableViewer;
@Inject
private MPart part;
@PostConstruct
public void createComposite(Composite parent) {
parent.setLayout(new GridLayout(1, false));
Text txtInput = new Text(parent, SWT.BORDER);
txtInput.setMessage("Enter text to mark part as dirty");
txtInput.addModifyListener(e -> part.setDirty(true));
txtInput.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
tableViewer = new TableViewer(parent);
tableViewer.setContentProvider(ArrayContentProvider.getInstance());
tableViewer.setInput(createInitialDataModel());
tableViewer.getTable().setLayoutData(new GridData(GridData.FILL_BOTH));
}
3-1. Model Resources
모델 요소들은 URI(Uniform Resource Identifier) 통한 정적 리소스나 클래스를 연결 시킬 수 있다. 이렇게 하기 위해 이클립스는 두가지 URI 패턴을 정의한다. 이클립스는 대부분 참조하고 있는 객체들이나 리소스들을 늦게 초기화 한다. 예를들어 어떤 파트에 연결되어 있는 클래스는 파트가 실제 보이게 될 때 초기화 된다.
위의 예제의 Sample Part는 bundleclass://gef5Test/gef5test.parts.SamplePart 라는 Uri로 정의되어 있다. 이때 “bundleclass://” 부분은 고정 값이며, “gef5Test”는 프로젝트 네임, “gef5test.parts.”는 패키지명이다.
예를들어 Class URI 속성을 갖는 어떤 파트는 그 속성에 "bundleclass://URI" 방식으로 벨류를 입력하여 Java 클래스를 연결할 수 있다. 이 클래스는 파트의 행위를 제공하게 된다. 이 연결된 객체는 이클립스 프레임웍이 생성시킨다. 이전 설명에서 집/방 을 개념을 이용해서 은유적으로 설명했던 부분을 떠올려 보면, 이 클래스는 가구배치와 방의 레이아웃을 정의할 책임을 갖으며, 사용자와 상호작용 할 객체들이 어떻게 동작해야할지 정의 할 책임또한 갖는다.
추가적으로 Part 클래스들은 다른 클래스를 확장해서는 안되며, 어떠한 인터페이스도 구현해서는 안된다.
4. Available model objects
Application.e4xmi 파일에 저장되어 있는 사용자 변경 사항 및 모델에 기여하는 것들과 같은 애플리케이션 모델에 관한 정보를 파싱하며 이와 같은 정보들을 실행하는 동안에 자바 객체에 저장해둔다. 이 객체 들을 모델 객체라고 부르며, 실행하는 동안 모델 요소들의 속성들을 표시합니다.
모델 객체 유형 리스트
Model element |
Description |
MApplication |
애플리케이션 객체를 설명한다. 다른 요소들 모두 이 객체에 포함된다. |
MAddon |
보통 사용자 인터페이스가 아닌 독립적인 컴포넌트. 애플리케이션 라이프 사이클내에서 발생하는 이벤트들을 등록하고 이 이벤트들을 처리할 수 있다. |
MWindow |
애플리케이션에 하나의 윈도우를 나타낸다. |
MTrimmedWindow |
MWindow 와 유사하지만, TrimBars 모델 요소를 이용하여 윈도우에 툴바를 포함할 수 있다. |
MPerspective |
윈도우내에 표시될 파트들이 용도에 따라 서로다르게 배치 되도록 할 수 있는 요소다. MPerspectiveStack 내에 있어야 한다. |
MPart |
뷰나 에디터 같은 파트 요소다. |
MDrityable |
주입될 수 있는 MPart 의 속성이다. true 로 설정하면, 이 속성은 이클립스 플랫폼에게 이 파트가 저장되지 않은 데이터를 포함하고 있다고 알리게 된다.(is dirty). 핸들러에서 우리는 저장이 필요한지를 확인하기 위해 이 속성을 확인할 수 있다. |
MPartDescriptor |
MPartDescriptor 는 새 파트를 만들기 위한 템플릿이다. 이클립스 프레임워크는 이 파트 디스크립터 정보를 가지고 새 파트를 만들고 디스플레이 할 수 있다. |
Snippets |
Snippets 은 우리가 프로그램을 통해 생성하고 싶은 모델 파트들을 미리 구성하는데 사용될 수 있다. 우리는 하나의 snippet 을 복사하고 그것을 실행시간에 애플리케이션 모델에 추가하기 위해 이클립스 프레임워크를 사용할 수 있다. |
5. SWT Browser widget
RCP 4에서는 Part Class에 SWT를 사용할 수 있다.
package com.example.e4.rcp.todo.parts;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.annotation.PostConstruct;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
public class PlaygroundPart {
private Text text;
private Browser browser;
@PostConstruct
public void createControls(Composite parent) {
parent.setLayout(new GridLayout(2, false));
text = new Text(parent, SWT.BORDER);
text.setMessage("Enter City");
text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
Button button = new Button(parent, SWT.PUSH);
button.setText("Search");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String city = text.getText();
if (city.isEmpty()) {
return;
}
try {
// not supported at the moment by Google
// browser.setUrl("http://maps.google.com/maps?q="
// + URLEncoder.encode(city, "UTF-8")
// + "&output=embed");
browser.setUrl("https://www.google.com/maps/place/"
+ URLEncoder.encode(city, "UTF-8")
+ "/&output=embed");
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
}
});
browser = new Browser(parent, SWT.NONE);
browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
}
@Focus
public void onFocus() {
text.setFocus();
}
}
위는 구글 맵 API를 사용하는 코드로 PlaygroundPart를 Application.e4xmi의 Windows에서 참조하여 사용할 수 있다.
6. Dependency injection
이클립스에서 프로그래밍할 때 사용되는 모델은 생성자, 메소드, 필드 주입을 지원하는데, 이것은 Java Specification Request 330 (JSR 330)을 따르고 있다.
이에 따라 이클립스에서는 의존성 주입을 목적으로 하는 어노테이션을 정의한다.
따라서 이클립스 종속성 프레임워크는 주입되는 객체의 키와 타입이 정확한지를 확인한다.
예를 들어, @Inject @Named(“xyz”) Todo todo; 라는 코드가 있다면 xyz키에 해당하는 Todo 타입의 객체를 사용하고 싶다는 의미로 프레임워크에서는 할당할 수 있는 타입을 찾았을 경우에 하나의 객체만 주입한다는 의미이다.
Injection에 대해서는 아래 링크를 참고
Annotation |
Description |
@javax.inject.Inject |
JSR330 에서 정의하고 있으며, 필드, 생성자, 메소드에 추가될 수 있다. 이클립스 프레임워크는 필드나 파라미터 인스턴스들을 해당하는 객체에 주입한다. |
@javax.inject.Named |
JSR330 에서 정의하고 있으며, 값을 주입하기 위한 키를 정의한다. 기본적으로, 클래스 풀네임이(qualified class name) 키로 사용된다. 기본값을 위한 여러 키들이 IServiceConstants interface 에 상수로 정의되어 있다. |
@Optional |
이클립스 애노테이션은(Eclipse specific annotation) 주입되는 값을 옵셔널로 표시한다. 주어진 키에(또는 타입) 대한 유효한 값이 없을 경우라도 프레임워크는 예외를 던지지 않는다. |
@GroupUpdates |
이클립스 애노테이션은 (Eclipse specific annotation) @Inject 가 일괄 실행되기 위한 업데이트를 표시한다. 우리가 이클립스 컨텍스트 상에서 이러한 객체들을 변경하면, IEclipseContext 객체의 processWaiting() 메소드는 update 를 시작시킨다. 이 에노테이션은 퍼포먼스 최적화를 위해 플랫폼이 사용하는 것으로 만들어 졌으며, 드믈게 RCP 애플리케이션들에서느 필요할 것이다. |
7. Eclipse Context Life Cycle
이클립스 애플리케이션이 시작되는 동안 이클립스 런타임은 IEclipseContext interace 를 구현하는 하나의 객체를 생성한다. 이 객체를 context 또는 Eclipse context 라고 부른다.
context는 객체들이 특정 키 아래에 위치될 수 있다는 점에서 Map 데이터 구조와 유사하다. 키는 하나의 String 이며, 많은 경우에 풀네임의 클래스 명이 키로 사용된다. 키가 가리키는 값은 다른 객체들안으로 주입될 수 있다. 하지만 map 과 달리, 이클립스 context 는 계층적이며 또한 요청했던 키에 대한 값을 동적으로 계산할 수 있다.
다른 context 객체들은 우리의 애플리케이션 모델 구조를 기반으로 계층적인 트리 구조를 구성하기 위해 연결된다. 이 계층에서 가장 높은 레벨은 application context이다.
context hierarchy
8. Active Part or Shell
이클립스 플랫폼은 현재 선택된 파트와 애플리케이션 객체의 IEclipseContext 안으로 액티브 셸을 위치시킨다. 관련되는 키들은 IServiceConstants interface 에 정의된다.
@Inject
@Optional
public void receiveActivePart(
@Named(IServiceConstants.ACTIVE_PART) MPart activePart) {
if (activePart != null) {
System.out.println("Active part changed "
+ activePart.getLabel());
}
}
active shell 을 추적하기 위해서는 IServiceConstants.ACTIVE_SHELL 키를 사용한다.
// tracks the active shell
@Inject
@Optional
public void receiveActiveShell(
@Named(IServiceConstants.ACTIVE_SHELL) Shell shell) {
if (shell != null) {
System.out.println("Active shell (Window) changed");
}
}
9. Eclipse life cycle annotations for parts
Annotation |
Description |
@PostConstruct |
클래스가 구성되고, 맴버변수와 메소드 인젝션이 수행된 후에 호출된다. |
@PreDestroy |
클래스가 소멸되기 전에 호출된다. 리소스들을 해제하는데 사용될 수 있다. |
@Focus |
파트가 포커싱 될 때마다 호출된다. |
@Persist |
이클립스 프레임워크가 파트에 저장요청을 하게 되면 호출된다. |
@PersistState |
파트가 인스턴스상태를 저장할수 있도록, 모델객체가 해제되기 전에 호출된다. 이 메소드는 @PreDestroy 메소드가 호출되기전에 호출된다. |
RCP 4에서는 파트의 사용자 인터페이스, 즉 UI를 구성하는 Composite가 포함된 CreateControls는 @PostConstruct 애노테이션을 붙인 메소드에서 구성하는 것을 추천한다고 한다.
import javax.annotation.PostConstruct;
import org.eclipse.swt.widgets.Composite;
// more code
@PostConstruct
public void createControls(Composite parent) {
System.out.println(this.getClass().getSimpleName()
+ " @PostConstruct method called.");
}
10. Handler와 Commands의 사용
하나의 커맨드에는 save, edit, copy 와 같은기능을 수행하기 위한 액션들이 정의된다.
이런 커맨드의 행위는 Handler를 이용해서 정의한다. Handler 모델 요소는 contributionURI 속성을 이용해서 연결되는 클래스를 가리킨다. 이 속성은 모델 에디터 창에서 Class URL로 표시된다.
핸들러 클래스 내에서는 하나의 메소드에만 @Execute를 사용할 수 있고, 다른 하나의 메소드에만 @CanExecute를 사용할 수 있다.
Annotation |
Description |
@Execute |
핸들러에서 액션을 책임질 메소드에 표시한다. 프레임워크는 사용자 인터페이스와 관련되는 매뉴 엔트리 등이 선택될 경우 이 메소드를 한번 실행시킨다. |
@CanExecute |
이클립스 프레임워크가 핸들러가 현재 실행 가능한지를 체크하기 위해 사용할 메소드에 표시해라. 핸들러 클래스의 이 메소드가 false 를 반환하면 이클립스는 해당하는 사용자 인터페이스를 비활성화 시킨다. 예를들어 핸들러 클래스의 @CanExecute 가 표시된 메소드에서 true 를 반환하면 저장 버튼은 활성화 된다. 이 메소드의 기본 반환값은 true 이기 때문에 핸들러 클래스는 항상 실행될 수 있다. @CanExecute 메소드는 반드시 구현할 필요는 없다. |
package com.example.e4.rcp.todo.handlers;
// import statements cut out
// ..
public class ExitHandler {
@Execute
public void execute(IWorkbench workbench) {
workbench.close();
}
// NOT REQUIRED IN THIS EXAMPLE
// just to demonstrates the usage of
// the annotation
@CanExecute
public boolean canExecute() {
return true;
}
}
11. default ids for commonly used commands
Command |
ID |
Save |
org.eclipse.ui.file.save |
Save All |
org.eclipse.ui.file.saveAll |
Undo |
org.eclipse.ui.edit.undo |
Redo |
org.eclipse.ui.edit.redo |
Cut |
org.eclipse.ui.edit.cut |
Copy |
org.eclipse.ui.edit.copy |
Paste |
org.eclipse.ui.edit.paste |
Delete |
org.eclipse.ui.edit.delete |
Import |
org.eclipse.ui.file.import |
Export |
org.eclipse.ui.file.export |
Select All |
org.eclipse.ui.edit.selectAll |
About |
org.eclipse.ui.help.aboutAction |
Preferences |
org.eclipse.ui.window.preferences |
Exit |
org.eclipse.ui.file.exit |
'Java > RCP&GEF' 카테고리의 다른 글
Eclipse RCP란? (0) | 2020.07.27 |
---|---|
Eclipse RCP SWT Image Path 지정 방법 (0) | 2020.07.27 |
[GEF] Eclipse GEF 3.11 다운로드 방법 (0) | 2020.04.02 |
Eclipse GEF - Editor를 화면에 띄우는 방법 (0) | 2020.04.02 |
Eclipse RCP - plugin.xml에서 View의 데이터 다른 View로 옮기기 (0) | 2020.04.01 |