[Eclipse] 실전 플러그인 개발: View와 가까워지기

Ma_Seokjae·2024년 7월 19일
post-thumbnail

Eclipse UI

Eclipse UI는 다양한 플러그인들이 통합되어 동작하는 Eclipse 플랫폼의 사용자 인터페이스를 의미합니다. 주요 구성 요소는 Workbench, Perspective, View, Editor, Menu, Toolbar 등이 있습니다. Workbench는 Eclipse의 최상위 창으로, 여러 개의 Perspective와 View, Editor가 포함될 수 있습니다.

View

View는 Eclipse Workbench의 UI 구성 요소로, 사용자에게 특정 정보를 표시하고 상호작용을 제공합니다. View는 일반적으로 ViewPart의 하위 클래스로 구현됩니다. Eclipse의 늦은 초기화 원칙에 따라, IWorkbenchPage 인스턴스는 IViewReference 인스턴스를 유지합니다. 이는 View가 필요할 때 로드되도록 합니다.

View와 Editor의 차이점

  • View: 특정 데이터를 표시하고 상호작용을 제공하는 UI 요소입니다. 여러 View가 한 번에 열릴 수 있으며, 일반적으로 도구 창으로 사용됩니다.

  • Editor: 파일이나 문서를 편집하는 데 사용됩니다. 하나의 파일에 대해 하나의 Editor만 열 수 있으며, 주로 중앙 영역을 차지합니다.

View Category 및 View 선언

Eclipse 플러그인은 plugin.xml 파일에서 View를 선언합니다. View는 특정 카테고리에 속할 수 있으며, 이를 통해 View를 쉽게 찾고 관리할 수 있습니다.

[plugin.xml 예제]

<extension
      point="org.eclipse.ui.views">
   <category
         name="My Category"
         id="com.example.category">
   </category>
   <view
         name="My View"
         icon="icons/sample.gif"
         category="com.example.category"
         class="com.example.MyView"
         id="com.example.myview">
   </view>
</extension>

View Part

ViewPart 클래스는 View를 구현하는 기본 클래스입니다. 주요 메서드로는 createPartControl(Composite), dispose(), getAdapter(Class), saveState(IMemento), setFocus() 등이 있습니다.

  • createPartControl(Composite): View의 컨트롤을 생성하고 초기화하는 메서드

  • dispose(): View가 닫힐 때 호출되는 메서드로, 리소스를 정리

  • getAdapter(Class): View가 특정 인터페이스를 구현하도록 함

  • saveState(IMemento): View의 상태를 저장

  • setFocus(): View에 포커스를 설정

[예제]

package com.example;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.part.ViewPart;

public class MyView extends ViewPart {
   public static final String ID = "com.example.myview";

   @Override
   public void createPartControl(Composite parent) {
      Label label = new Label(parent, SWT.NONE);
      label.setText("Hello, Eclipse View!");
   }

   @Override
   public void setFocus() {
      // Focus 설정 코드
   }

   @Override
   public void dispose() {
      // 리소스 정리 코드
      super.dispose();
   }

   @Override
   public Object getAdapter(Class adapter) {
      // 어댑터 반환 코드
      return super.getAdapter(adapter);
   }

   @Override
   public void saveState(IMemento memento) {
      // 상태 저장 코드
      super.saveState(memento);
   }
}

IAdaptable

IAdaptable 인터페이스는 객체가 다른 형식으로 변환될 수 있도록 합니다. getAdapter(Class) 메서드를 통해 다양한 타입의 어댑터를 반환할 수 있습니다.

IAdapterFactory

IAdapterFactory는 어댑터 팩토리를 구현하는 인터페이스로, 객체를 다양한 타입으로 변환하는 역할을 합니다. 어댑터 팩토리는 Activator의 start() 메서드에서 등록되고 stop() 메서드에서 해제됩니다.

[예제]

package com.example;

import org.eclipse.core.runtime.IAdapterFactory;
import org.eclipse.ui.IWorkbenchWindow;

public class MyAdapterFactory implements IAdapterFactory {
   @Override
   public Object getAdapter(Object adaptableObject, Class adapterType) {
      if (adapterType == IWorkbenchWindow.class && adaptableObject instanceof MyView) {
         return ((MyView) adaptableObject).getViewSite().getWorkbenchWindow();
      }
      return null;
   }

   @Override
   public Class[] getAdapterList() {
      return new Class[] { IWorkbenchWindow.class };
   }
}

Activator에서 등록 및 해제:

package com.example;

import org.eclipse.core.runtime.Platform;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

public class Activator extends AbstractUIPlugin {
   private static Activator plugin;

   @Override
   public void start(BundleContext context) throws Exception {
      super.start(context);
      plugin = this;
      Platform.getAdapterManager().registerAdapters(new MyAdapterFactory(), MyView.class);
   }

   @Override
   public void stop(BundleContext context) throws Exception {
      plugin = null;
      super.stop(context);
   }

   public static Activator getDefault() {
      return plugin;
   }
}

풀다운 메뉴

풀다운 메뉴는 View의 도구 모음에 추가할 수 있습니다. IWorkbenchPartSitegetActionBars() 메서드를 사용하여 풀다운 메뉴를 설정합니다.

IMenuManager menuManager = getViewSite().getActionBars().getMenuManager();
menuManager.add(new Action("My Action") {
   @Override
   public void run() {
      // 액션 실행 코드
   }
});

키보드 액션

키보드 액션은 KeyListener를 등록하여 특정 키 입력에 반응하도록 설정할 수 있습니다.

parent.getShell().getDisplay().addFilter(SWT.KeyDown, event -> {
   if (event.keyCode == SWT.F5) {
      // F5 키가 눌렸을 때 실행할 코드
   }
});

전역 액션

전역 액션은 IWorkbenchActionConstants에 정의된 상수를 사용하여 설정할 수 있습니다.

getViewSite().getActionBars().setGlobalActionHandler(ActionFactory.CUT.getId(), new Action() {
   @Override
   public void run() {
      // Cut 액션 실행 코드
   }
});

클립보드 액션

클립보드 액션을 수행하기 위해서는 Clipboard 객체가 필요합니다.

Clipboard clipboard = new Clipboard(Display.getCurrent());
String textData = "Clipboard Data";
clipboard.setContents(new Object[] { textData }, new Transfer[] { TextTransfer.getInstance() });

String result = (String) clipboard.getContents(TextTransfer.getInstance());
clipboard.dispose();

드래그 앤 드롭

드래그 앤 드롭은 DragSourceDropTarget을 사용하여 설정합니다.

// 드래그 소스 설정
DragSource dragSource = new DragSource(table, DND.DROP_MOVE);
dragSource.setTransfer(new Transfer[] { TextTransfer.getInstance() });
dragSource.addDragListener(new DragSourceAdapter() {
   @Override
   public void dragSetData(DragSourceEvent event) {
      event.data = "Dragged Data";
   }
});

// 드롭 타겟 설정
DropTarget dropTarget = new DropTarget(table, DND.DROP_MOVE);
dropTarget.setTransfer(new Transfer[] { TextTransfer.getInstance() });
dropTarget.addDropListener(new DropTargetAdapter() {
   @Override
   public void drop(DropTargetEvent event) {
      String data = (String) event.data;
      // 드롭된 데이터 처리 코드
   }
});

상태 저장

View의 상태는 IMemento 객체를 통해 저장하고 로드할 수 있습니다.

@Override
public void saveState(IMemento memento) {
   memento.putString("key", "value");
}

@Override
public void init(IViewSite site, IMemento memento) throws PartInitException {
   super.init(site, memento);
   if (memento != null) {
      String value = memento.getString("key");
      // 상태 값 로드 처리 코드
   }
}

간단한 시나리오를 통해 정리

우선 시나리오 전체 코드를 제공하도록 하겠습니다. 이클립스 뷰에서 자주 사용하는 기능들 중심으로 작성을 하였는데요. 시나리오 순서대로 차례대로 더 자세히 설명해드리도록 하겠습니다.

[MyView.java]

package com.example.myplugin.views;

import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Action;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;

public class MyView extends ViewPart {
    public static final String ID = "com.example.myplugin.views.MyView";
    
    // 시나리오 2: View에 플다운 메뉴 정의
    private Action action1;
    private Action action2;
    private Action action3;
    
    // 시나리오 3: View에 키보드 액션 추가
    private Text text;
    
    // 시나리오 4: 전역 액션 추가
    private Action cutAction;
    private Action copyAction;
    private Action pasteAction;
    
    // 시나리오 4_1: 전역 액션 실질적 기능 구현
    private Clipboard clipboard;

    @Override
    public void createPartControl(Composite parent) {
        // 시나리오 4_1: 전역 액션 실질적 기능 구현
        clipboard = new Clipboard(parent.getDisplay());
    	
        // 시나리오 2: View에 플다운 메뉴 정의
        createActions();
        initializeToolBar();
        initializeMenu();
        
        // 시나리오 3: View에 키보드 액션 추가
        text = new Text(parent, SWT.BORDER);
        text.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == SWT.CR) {
                    // Enter 키가 눌렸을 때의 동작
                }
            }
        });
        
        // 시나리오 4: 전역 액션 추가
        registerGlobalActions();
        
        // 시나리오 5: 드래그 앤 드롭 기능 추가
        addDragAndDropSupport();
    }
    
    // 시나리오 2: View에 플다운 메뉴 정의
    private void createActions() {
        action1 = new Action() {
            @Override
            public void run() {
                // Action1 실행 코드
            	 System.out.println("Action 1 executed");
            }
        };
        action1.setText("Action 1");
        action1.setToolTipText("Action 1 tooltip");

        action2 = new Action() {
            @Override
            public void run() {
                // Action2 실행 코드
            	System.out.println("Action 2 executed");
            }
        };
        action2.setText("Action 2");
        action2.setToolTipText("Action 2 tooltip");
        
        action3 = new Action() {
            @Override
            public void run() {
                // Action3 실행 코드
            	System.out.println("Action 3 executed");
            }
        };
        action3.setText("Action 3");
        action3.setToolTipText("Action 3 tooltip");
        
        // 시나리오 4: 전역 액션 추가 || 시나리오 4_1: 전역 액션 실질적 기능 구현
        cutAction = new Action() {
            @Override
            public void run() {
                // 잘라내기 동작을 정의
                String selectedText = text.getSelectionText();
                if (!selectedText.isEmpty()) {
                    clipboard.setContents(new Object[] { selectedText }, new Transfer[] { TextTransfer.getInstance() });
                    text.insert(""); // 선택된 텍스트를 잘라내기 위해 삭제
                    System.out.println("잘라내기 동작 실행됨");
                }
            }
        };
        cutAction.setText("잘라내기");

        copyAction = new Action() {
            @Override
            public void run() {
                // 복사 동작을 정의
                String selectedText = text.getSelectionText();
                if (!selectedText.isEmpty()) {
                    clipboard.setContents(new Object[] { selectedText }, new Transfer[] { TextTransfer.getInstance() });
                    System.out.println("복사 동작 실행됨");
                }
            }
        };
        copyAction.setText("복사");

        pasteAction = new Action() {
            @Override
            public void run() {
                // 붙여넣기 동작을 정의
                String clipboardText = (String) clipboard.getContents(TextTransfer.getInstance());
                if (clipboardText != null) {
                    text.insert(clipboardText);
                    System.out.println("붙여넣기 동작 실행됨");
                }
            }
        };
        pasteAction.setText("붙여넣기");
    }
    
    @Override
    public void dispose() {
        clipboard.dispose();
        super.dispose();
    }
    
    // 시나리오 2: View에 플다운 메뉴 정의
    private void initializeToolBar() {
        IToolBarManager toolbarManager = getViewSite().getActionBars().getToolBarManager();
        toolbarManager.add(action1);
        toolbarManager.add(action2);
        toolbarManager.add(action3);
    }
    
    // 시나리오 2: View에 플다운 메뉴 정의
    private void initializeMenu() {
        IMenuManager menuManager = getViewSite().getActionBars().getMenuManager();
        menuManager.add(action1);
        menuManager.add(action2);
        menuManager.add(action3);
    }
    
    // 시나리오 4: 전역 액션 추가
    private void registerGlobalActions() {
    	IViewSite site = getViewSite();
        IActionBars actionBars = site.getActionBars();
        actionBars.setGlobalActionHandler(ActionFactory.CUT.getId(), cutAction);
        actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(), copyAction);
        actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(), pasteAction);
    }
    
    // 시나리오 5: 드래그 앤 드롭 기능 추가
    private void addDragAndDropSupport() {
        DragSource dragSource = new DragSource(text, DND.DROP_MOVE | DND.DROP_COPY);
        dragSource.setTransfer(new TextTransfer[] { TextTransfer.getInstance() });
        dragSource.addDragListener(new DragSourceListener() {
            @Override
            public void dragStart(DragSourceEvent event) {
            }

            @Override
            public void dragSetData(DragSourceEvent event) {
                event.data = text.getText();
            }

            @Override
            public void dragFinished(DragSourceEvent event) {
                if (event.detail == DND.DROP_MOVE) {
                    text.setText("");
                }
            }
        });
        
        DropTarget dropTarget = new DropTarget(text, DND.DROP_MOVE | DND.DROP_COPY);
        dropTarget.setTransfer(new TextTransfer[] { TextTransfer.getInstance() });
        dropTarget.addDropListener(new DropTargetListener() {
            @Override
            public void dragEnter(DropTargetEvent event) {
            }

            @Override
            public void dragLeave(DropTargetEvent event) {
            }

            @Override
            public void dragOperationChanged(DropTargetEvent event) {
            }

            @Override
            public void dragOver(DropTargetEvent event) {
            }

            @Override
            public void drop(DropTargetEvent event) {
                if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) {
                    text.setText((String) event.data);
                }
            }

            @Override
            public void dropAccept(DropTargetEvent event) {
            }
        });
    }
    
    @Override
    public void setFocus() {
        text.setFocus();
    }
}

[시나리오 1: 기본 View 생성]
첫 번째 시나리오에서는 가장 기본적인 View를 생성하고 간단한 Label을 추가합니다.

목표: 간단한 View 생성 및 Label 추가

1. Eclipse 플러그인 프로젝트 생성

  • File > New > Project > Plug-in Project 선택
  • 프로젝트 이름을 입력하고 Finish 클릭

2. View 선언

  • plugin.xml 파일을 열고 Extensions 탭으로 이동
  • org.eclipse.ui.views 확장을 추가하고 다음과 같이 View를 선언:

[plugin.xml]

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
   <extension
    	 point="org.eclipse.ui.views">
	   <view
	         id="com.example.myplugin.views.MyView"
	         name="My View"
	         class="com.example.myplugin.views.MyView"
	         category="com.example.myplugin">
	   </view>
	</extension>
</plugin>

3. View 클래스 생성

  • src 디렉토리에서 com.example.myplugin.views 패키지를 생성하고 MyView 클래스를 추가:
profile
Why not change the code?

0개의 댓글