SWT (Standard Widget Toolkit)

Eclipse Foundationが開発するJava用GUIツールキット。各プラットフォームのネイティブウィジェットを活用し、OSに最適化されたパフォーマンスと外観を実現。Eclipse IDEの基盤技術としても使用。

デスクトップJavaGUIEclipseネイティブクロスプラットフォーム

フレームワーク

SWT (Standard Widget Toolkit)

概要

SWT(Standard Widget Toolkit)は、Eclipseプロジェクトで開発されたJava用のGUIツールキットです。ネイティブのOSウィジェットを直接利用することで、プラットフォーム固有のルックアンドフィールとパフォーマンスを実現します。Eclipse IDEの基盤技術として開発され、現在も多くのEclipse系アプリケーションで使用されています。

詳細

2025年現在、SWTはEclipse Foundationのもとで継続的に開発されており、Eclipse IDE 2025年版を含む最新のEclipse RCP(Rich Client Platform)アプリケーションの基盤として活用されています。JavaのSwingとは根本的に異なるアプローチを採用しています。

SWTの主要な特徴:

  • ネイティブウィジェット: WindowsではWin32 API、macOSではCocoa、LinuxではGTK+を直接使用
  • 高パフォーマンス: OSネイティブレンダリングによる高速描画
  • 明示的リソース管理: メモリリークを防ぐためのdispose()パターン
  • プラットフォーム統合: 各OSの標準ウィジェットと完全に統合
  • Eclipse RCP: 豊富なプラグインアーキテクチャとワークベンチフレームワーク
  • OSGI対応: モジュラーアプリケーション開発のサポート

活用分野:

  • Eclipse IDE(最も著名な利用例)
  • IBM Watson Studio、IBM Notes
  • SAP Development Tools
  • 科学技術アプリケーション(NASA、研究機関)
  • 金融系デスクトップアプリケーション
  • 産業用制御ソフトウェア

一方で、SwingやJavaFXと比較すると学習コストが高く、プラットフォーム固有のネイティブライブラリ管理、リソースの明示的な解放が必要という課題があります。また、モバイル対応は限定的で、コミュニティサイズも相対的に小さいです。

メリット・デメリット

メリット

  • ネイティブパフォーマンス: OSのネイティブウィジェットを使用し高速動作
  • プラットフォーム統合: 各OSのルックアンドフィールを自然に再現
  • メモリ効率: 適切にリソース管理すれば軽量な動作
  • Eclipse統合: Eclipse RCP(Rich Client Platform)での豊富な機能
  • プロフェッショナル利用: 企業向けアプリケーションでの実績
  • ネイティブ機能: OS固有のAPIへの直接アクセス可能

デメリット

  • 複雑なリソース管理: メモリリークを避けるための明示的なdispose必須
  • 学習コストの高さ: Swingより複雑なプログラミングモデル
  • 大きな依存関係: プラットフォーム固有のネイティブライブラリが必要
  • 限定的なコミュニティ: Swing/JavaFXと比較してコミュニティが小さい
  • デバッグの困難さ: ネイティブ部分のデバッグが複雑
  • 配布の複雑さ: プラットフォーム固有のライブラリ配布が必要

参考ページ

書き方の例

Hello World

import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;

public class HelloWorldSWT {
    public static void main(String[] args) {
        // ディスプレイとシェルの作成
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("Hello World - SWT");
        shell.setLayout(new FillLayout());
        
        // ラベルの作成
        Label label = new Label(shell, SWT.CENTER);
        label.setText("Hello, SWT World!");
        
        // シェルのサイズ設定と表示
        shell.setSize(400, 300);
        shell.open();
        
        // イベントループ
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        
        // リソースの解放
        display.dispose();
    }
}

基本的なレイアウト

import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;

public class LayoutExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("Layout Example");
        shell.setLayout(new BorderLayout());
        
        // ツールバー(上部)
        ToolBar toolBar = new ToolBar(shell, SWT.HORIZONTAL);
        ToolItem newItem = new ToolItem(toolBar, SWT.PUSH);
        newItem.setText("新規");
        ToolItem openItem = new ToolItem(toolBar, SWT.PUSH);
        openItem.setText("開く");
        ToolItem saveItem = new ToolItem(toolBar, SWT.PUSH);
        saveItem.setText("保存");
        
        // メインエリア(中央)
        Composite centerComposite = new Composite(shell, SWT.NONE);
        centerComposite.setLayout(new FillLayout());
        Text textArea = new Text(centerComposite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
        textArea.setText("ここにテキストを入力してください...");
        
        // ステータスバー(下部)
        Label statusBar = new Label(shell, SWT.BORDER);
        statusBar.setText("準備完了");
        
        // レイアウトデータの設定
        toolBar.setLayoutData(BorderData.NORTH);
        centerComposite.setLayoutData(BorderData.CENTER);
        statusBar.setLayoutData(BorderData.SOUTH);
        
        shell.setSize(600, 400);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}

イベントハンドリング

import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.SWT;

public class EventHandlingExample {
    private int counter = 0;
    private Label counterLabel;
    
    public static void main(String[] args) {
        new EventHandlingExample().run();
    }
    
    private void run() {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("Event Handling Example");
        shell.setLayout(new GridLayout(2, false));
        
        // カウンター表示ラベル
        counterLabel = new Label(shell, SWT.NONE);
        counterLabel.setText("カウンター: 0");
        GridData labelData = new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1);
        counterLabel.setLayoutData(labelData);
        
        // インクリメントボタン
        Button incrementButton = new Button(shell, SWT.PUSH);
        incrementButton.setText("増加");
        incrementButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                counter++;
                updateCounterLabel();
            }
        });
        
        // デクリメントボタン
        Button decrementButton = new Button(shell, SWT.PUSH);
        decrementButton.setText("減少");
        decrementButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                counter--;
                updateCounterLabel();
            }
        });
        
        // リセットボタン
        Button resetButton = new Button(shell, SWT.PUSH);
        resetButton.setText("リセット");
        resetButton.addSelectionListener(e -> {
            counter = 0;
            updateCounterLabel();
        });
        GridData resetData = new GridData(SWT.CENTER, SWT.CENTER, true, false, 2, 1);
        resetButton.setLayoutData(resetData);
        
        shell.pack();
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
    
    private void updateCounterLabel() {
        counterLabel.setText("カウンター: " + counter);
    }
}

テーブルとデータ表示

import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;

public class TableExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("Table Example");
        shell.setLayout(new GridLayout());
        
        // テーブルの作成
        Table table = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
        table.setHeaderVisible(true);
        table.setLinesVisible(true);
        GridData tableData = new GridData(SWT.FILL, SWT.FILL, true, true);
        tableData.heightHint = 200;
        table.setLayoutData(tableData);
        
        // カラムの作成
        String[] columnTitles = {"ID", "名前", "年齢", "部署"};
        for (String title : columnTitles) {
            TableColumn column = new TableColumn(table, SWT.NONE);
            column.setText(title);
            column.setWidth(100);
        }
        
        // データの追加
        String[][] data = {
            {"1", "田中太郎", "30", "開発"},
            {"2", "佐藤花子", "28", "営業"},
            {"3", "鈴木一郎", "35", "企画"},
            {"4", "高橋美子", "32", "人事"}
        };
        
        for (String[] row : data) {
            TableItem item = new TableItem(table, SWT.NONE);
            item.setText(row);
        }
        
        // ボタンパネル
        Composite buttonComposite = new Composite(shell, SWT.NONE);
        buttonComposite.setLayout(new RowLayout());
        
        Button addButton = new Button(buttonComposite, SWT.PUSH);
        addButton.setText("追加");
        addButton.addSelectionListener(e -> {
            TableItem newItem = new TableItem(table, SWT.NONE);
            newItem.setText(new String[]{
                String.valueOf(table.getItemCount()), 
                "新規ユーザー", 
                "25", 
                "未定"
            });
        });
        
        Button deleteButton = new Button(buttonComposite, SWT.PUSH);
        deleteButton.setText("削除");
        deleteButton.addSelectionListener(e -> {
            int selectionIndex = table.getSelectionIndex();
            if (selectionIndex != -1) {
                table.remove(selectionIndex);
            }
        });
        
        shell.setSize(500, 350);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}

ダイアログとメッセージボックス

import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;

public class DialogExample {
    public static void main(String[] args) {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("Dialog Example");
        shell.setLayout(new GridLayout(2, false));
        
        // メッセージダイアログボタン
        Button messageButton = new Button(shell, SWT.PUSH);
        messageButton.setText("メッセージダイアログ");
        messageButton.addSelectionListener(e -> {
            MessageBox messageBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
            messageBox.setMessage("これはメッセージです");
            messageBox.setText("情報");
            messageBox.open();
        });
        
        // 確認ダイアログボタン
        Button confirmButton = new Button(shell, SWT.PUSH);
        confirmButton.setText("確認ダイアログ");
        confirmButton.addSelectionListener(e -> {
            MessageBox confirmBox = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
            confirmBox.setMessage("削除してもよろしいですか?");
            confirmBox.setText("確認");
            int result = confirmBox.open();
            if (result == SWT.YES) {
                MessageBox resultBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
                resultBox.setMessage("削除されました");
                resultBox.setText("完了");
                resultBox.open();
            }
        });
        
        // 入力ダイアログボタン
        Button inputButton = new Button(shell, SWT.PUSH);
        inputButton.setText("入力ダイアログ");
        inputButton.addSelectionListener(e -> {
            InputDialog inputDialog = new InputDialog(shell, "入力", "名前を入力してください:", "", null);
            if (inputDialog.open() == InputDialog.OK) {
                String input = inputDialog.getValue();
                if (input != null && !input.trim().isEmpty()) {
                    MessageBox resultBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
                    resultBox.setMessage("こんにちは、" + input + "さん!");
                    resultBox.setText("挨拶");
                    resultBox.open();
                }
            }
        });
        
        // ファイルダイアログボタン
        Button fileButton = new Button(shell, SWT.PUSH);
        fileButton.setText("ファイルダイアログ");
        fileButton.addSelectionListener(e -> {
            FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
            fileDialog.setText("ファイルを選択");
            fileDialog.setFilterExtensions(new String[]{"*.txt", "*.java", "*.*"});
            fileDialog.setFilterNames(new String[]{"テキストファイル (*.txt)", "Javaファイル (*.java)", "すべてのファイル (*.*)"});
            String fileName = fileDialog.open();
            if (fileName != null) {
                MessageBox resultBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
                resultBox.setMessage("選択されたファイル: " + fileName);
                resultBox.setText("ファイル選択結果");
                resultBox.open();
            }
        });
        
        shell.pack();
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
}

// カスタム入力ダイアログクラス
class InputDialog extends Dialog {
    private String title;
    private String message;
    private String initialValue;
    private String value;
    
    public InputDialog(Shell parent, String title, String message, String initialValue, IInputValidator validator) {
        super(parent);
        this.title = title;
        this.message = message;
        this.initialValue = initialValue;
    }
    
    public int open() {
        Shell shell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
        shell.setText(title);
        shell.setLayout(new GridLayout(2, false));
        
        Label label = new Label(shell, SWT.NONE);
        label.setText(message);
        label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
        
        Text text = new Text(shell, SWT.BORDER);
        text.setText(initialValue != null ? initialValue : "");
        text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
        
        Button okButton = new Button(shell, SWT.PUSH);
        okButton.setText("OK");
        okButton.addSelectionListener(e -> {
            value = text.getText();
            shell.dispose();
        });
        
        Button cancelButton = new Button(shell, SWT.PUSH);
        cancelButton.setText("キャンセル");
        cancelButton.addSelectionListener(e -> {
            value = null;
            shell.dispose();
        });
        
        shell.pack();
        shell.open();
        
        Display display = shell.getDisplay();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        
        return value != null ? OK : CANCEL;
    }
    
    public String getValue() {
        return value;
    }
    
    public static final int OK = 0;
    public static final int CANCEL = 1;
}

メニューとツールバー

import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.SWT;

public class MenuToolbarExample {
    private Text textArea;
    
    public static void main(String[] args) {
        new MenuToolbarExample().run();
    }
    
    private void run() {
        Display display = new Display();
        Shell shell = new Shell(display);
        shell.setText("Menu and Toolbar Example");
        shell.setLayout(new BorderLayout());
        
        // メニューバーの作成
        Menu menuBar = new Menu(shell, SWT.BAR);
        shell.setMenuBar(menuBar);
        
        // ファイルメニュー
        MenuItem fileMenuItem = new MenuItem(menuBar, SWT.CASCADE);
        fileMenuItem.setText("ファイル(&F)");
        Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
        fileMenuItem.setMenu(fileMenu);
        
        MenuItem newMenuItem = new MenuItem(fileMenu, SWT.PUSH);
        newMenuItem.setText("新規(&N)\tCtrl+N");
        newMenuItem.setAccelerator(SWT.CTRL + 'N');
        newMenuItem.addSelectionListener(e -> newFile());
        
        MenuItem openMenuItem = new MenuItem(fileMenu, SWT.PUSH);
        openMenuItem.setText("開く(&O)\tCtrl+O");
        openMenuItem.setAccelerator(SWT.CTRL + 'O');
        openMenuItem.addSelectionListener(e -> openFile(shell));
        
        MenuItem saveMenuItem = new MenuItem(fileMenu, SWT.PUSH);
        saveMenuItem.setText("保存(&S)\tCtrl+S");
        saveMenuItem.setAccelerator(SWT.CTRL + 'S');
        saveMenuItem.addSelectionListener(e -> saveFile(shell));
        
        new MenuItem(fileMenu, SWT.SEPARATOR);
        
        MenuItem exitMenuItem = new MenuItem(fileMenu, SWT.PUSH);
        exitMenuItem.setText("終了(&X)");
        exitMenuItem.addSelectionListener(e -> shell.dispose());
        
        // 編集メニュー
        MenuItem editMenuItem = new MenuItem(menuBar, SWT.CASCADE);
        editMenuItem.setText("編集(&E)");
        Menu editMenu = new Menu(shell, SWT.DROP_DOWN);
        editMenuItem.setMenu(editMenu);
        
        MenuItem cutMenuItem = new MenuItem(editMenu, SWT.PUSH);
        cutMenuItem.setText("切り取り(&T)\tCtrl+X");
        cutMenuItem.setAccelerator(SWT.CTRL + 'X');
        cutMenuItem.addSelectionListener(e -> textArea.cut());
        
        MenuItem copyMenuItem = new MenuItem(editMenu, SWT.PUSH);
        copyMenuItem.setText("コピー(&C)\tCtrl+C");
        copyMenuItem.setAccelerator(SWT.CTRL + 'C');
        copyMenuItem.addSelectionListener(e -> textArea.copy());
        
        MenuItem pasteMenuItem = new MenuItem(editMenu, SWT.PUSH);
        pasteMenuItem.setText("貼り付け(&P)\tCtrl+V");
        pasteMenuItem.setAccelerator(SWT.CTRL + 'V');
        pasteMenuItem.addSelectionListener(e -> textArea.paste());
        
        // ツールバーの作成
        ToolBar toolBar = new ToolBar(shell, SWT.HORIZONTAL | SWT.FLAT);
        toolBar.setLayoutData(BorderData.NORTH);
        
        ToolItem newToolItem = new ToolItem(toolBar, SWT.PUSH);
        newToolItem.setText("新規");
        newToolItem.setToolTipText("新しいファイル");
        newToolItem.addSelectionListener(e -> newFile());
        
        ToolItem openToolItem = new ToolItem(toolBar, SWT.PUSH);
        openToolItem.setText("開く");
        openToolItem.setToolTipText("ファイルを開く");
        openToolItem.addSelectionListener(e -> openFile(shell));
        
        ToolItem saveToolItem = new ToolItem(toolBar, SWT.PUSH);
        saveToolItem.setText("保存");
        saveToolItem.setToolTipText("ファイルを保存");
        saveToolItem.addSelectionListener(e -> saveFile(shell));
        
        new ToolItem(toolBar, SWT.SEPARATOR);
        
        ToolItem cutToolItem = new ToolItem(toolBar, SWT.PUSH);
        cutToolItem.setText("切り取り");
        cutToolItem.addSelectionListener(e -> textArea.cut());
        
        ToolItem copyToolItem = new ToolItem(toolBar, SWT.PUSH);
        copyToolItem.setText("コピー");
        copyToolItem.addSelectionListener(e -> textArea.copy());
        
        ToolItem pasteToolItem = new ToolItem(toolBar, SWT.PUSH);
        pasteToolItem.setText("貼り付け");
        pasteToolItem.addSelectionListener(e -> textArea.paste());
        
        // テキストエリア
        textArea = new Text(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
        textArea.setLayoutData(BorderData.CENTER);
        textArea.setText("ここにテキストを入力してください...");
        
        // ステータスバー
        Label statusBar = new Label(shell, SWT.BORDER);
        statusBar.setText("準備完了");
        statusBar.setLayoutData(BorderData.SOUTH);
        
        shell.setSize(700, 500);
        shell.open();
        
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose();
    }
    
    private void newFile() {
        textArea.setText("");
    }
    
    private void openFile(Shell shell) {
        FileDialog dialog = new FileDialog(shell, SWT.OPEN);
        dialog.setFilterExtensions(new String[]{"*.txt", "*.*"});
        String fileName = dialog.open();
        if (fileName != null) {
            try {
                java.nio.file.Path path = java.nio.file.Paths.get(fileName);
                String content = java.nio.file.Files.readString(path);
                textArea.setText(content);
            } catch (Exception e) {
                MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
                errorBox.setMessage("ファイルの読み込みエラー: " + e.getMessage());
                errorBox.setText("エラー");
                errorBox.open();
            }
        }
    }
    
    private void saveFile(Shell shell) {
        FileDialog dialog = new FileDialog(shell, SWT.SAVE);
        dialog.setFilterExtensions(new String[]{"*.txt", "*.*"});
        String fileName = dialog.open();
        if (fileName != null) {
            try {
                java.nio.file.Path path = java.nio.file.Paths.get(fileName);
                java.nio.file.Files.writeString(path, textArea.getText());
                MessageBox infoBox = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
                infoBox.setMessage("ファイルが保存されました");
                infoBox.setText("保存完了");
                infoBox.open();
            } catch (Exception e) {
                MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
                errorBox.setMessage("ファイルの保存エラー: " + e.getMessage());
                errorBox.setText("エラー");
                errorBox.open();
            }
        }
    }
}