【UE4】エディタ拡張(リスト編)

投稿者: | 2017年1月10日

はじめに

あけましておめでとうございます!!
プログラマーの笹目です!

昨年は、記事を見て頂いた方、リツイート、いいね!してくださった方、ありがとうございました!
今年もUE4のエディタ拡張に関して少しずつ掲載していきます。
(今年はエディタ拡張以外の記事も挟んでいけたらなと思っております。。)

今回はリスト編です。
リスト状にウィジェットを配置する方法と
独自ウィジェットクラスの作成方法を見てみます。

※使用したバージョンはUE4.14.1になります。
※言語環境は英語になります。

【前回までの記事】
1. エディタ拡張(準備編)
2. エディタ拡張(ラベル編)
3. エディタ拡張(ボタン編)
4. エディタ拡張(レイアウト編)
5. エディタ拡張(インプットボックス編)

 


リストをつくる

リストを作成するには、SListViewウィジェットを使用します。
このウィジェットはテンプレートクラスになっているので、
例えば、文字列のリストを作る場合、変数の宣言は以下のようになります。

/** 文字列のリスト */
TSharedPtr<SListView<TSharedPtr<FString>>> ListViewWidget;

/** リスト要素の配列 */
TArray<TSharedPtr<FString>> Items;

レイアウトを作る場合は以下のようになります。

SAssignNew( ListViewWidget, SListView<TSharedPtr<FString>> )
.ItemHeight( 24 )
.ListItemsSource( &Items ) // このリストに表示するの要素の配列
.OnGenerateRow( this, &SList::OnGenerateRowForList )

OnGenerateRowイベントでは、リストに追加する行のレイアウトを指定します。

/**
 * リスト要素が生成されるときの処理
 */
TSharedRef<ITableRow> SList::OnGenerateRowForList(TSharedPtr<FString> Item, const TSharedRef<STableViewBase>& OwnerTable)
{
    return
        // リスト要素(行)を作成
        SNew( STableRow< TSharedPtr<FString> >, OwnerTable )
        .Padding( 2.f )
        [
            // ラベルとして要素を追加
            SNew( STextBlock ).Text( FText::FromString( *Item.Get() ) )
        ];
}


リスト用の独自ウィジェットクラスをつくる

リストはエディターを作成する場合には頻繁に使われる部品だと思います。
今までは、FTestWindowModule::OnSpawnPluginTab関数内に長々と
レイアウトを記述していましたが、
今回は独自ウィジェットクラスとして作成し、リストを簡単に記述できるようにしてみます。

今回はSListという以下のクラスを作成します。

【SList.h】

#include "SlateBasics.h"
 
class SList : public SCompoundWidget
{
public:
    SLATE_BEGIN_ARGS( SList ) {}
    SLATE_END_ARGS()
 
    /** コンストラクタ */ 
    void Construct( const FArguments& Args );
 
    /** 行追加イベント */
    TSharedRef<ITableRow> OnGenerateRowForList( TSharedPtr<FString> Item, const TSharedRef<STableViewBase>& OwnerTable );
    
    /** 要素追加処理 */
    void AddItem( const FString& item );
    
    /** リストクリア処理 */
    void ClearList();
 
 protected:
    /** ヘッダー */
    TSharedPtr<SHeaderRow> Header;

    /** リストビュー表示アイテム */
    TArray<TSharedPtr<FString>> Items;

    /** リストビューウィジェット */
    TSharedPtr< SListView<TSharedPtr<FString>> > ListViewWidget;
};

【SList.cpp】

#include "SList.h"

/**
 * コンストラクタ
 */
void SList::Construct( const FArguments& Args )
{
    ChildSlot
    [
        SNew( SVerticalBox )
        + SVerticalBox::Slot()
        .AutoHeight()
        [
            // ヘッダー追加
            SNew( SHeaderRow )
            + SHeaderRow::Column( TEXT("ListName") )
            .DefaultLabel( FText::FromString( TEXT("リスト") ) )
            .FillWidth( 1.f )
            .HAlignHeader( HAlign_Center )
        ]

        + SVerticalBox::Slot()
        [
            // スクロールボックス追加
            SNew( SScrollBox )
            + SScrollBox::Slot()
            [
                // リストビュー追加
                SAssignNew( ListViewWidget, SListView<TSharedPtr<FString>> )
                .ItemHeight( 24 )
                .ListItemsSource( &Items )
                .OnGenerateRow( this, &SList::OnGenerateRowForList )
            ]
        ]
    ];
}

/**
 * リスト要素が生成されるときの処理
 */
TSharedRef<ITableRow> SList::OnGenerateRowForList( TSharedPtr<FString> Item, const TSharedRef<STableViewBase>& OwnerTable )
{
    return
        // リスト要素(行)を作成
        SNew( STableRow< TSharedPtr<FString> >, OwnerTable)
        .Padding( 2.f )
        [
            SNew( STextBlock ).Text( FText::FromString( *Item.Get() ) )
        ];
}

/**
 * 要素追加処理
 */
 void SList::AddItem( const FString& item )
{
    // 新規追加
    Items.Add( MakeShareable( new FString( item ) ) );

    // リストビュー更新
    ListViewWidget->RequestListRefresh();
}

/**
 * リストクリア処理
 */
 void SList::ClearList()
{
    // 要素配列クリア
    Items.Empty();

    // リストビュー更新
    ListViewWidget->RequestListRefresh();
}


使ってみる

では実際に使ってみます。
リストに要素を動的に追加・削除したいので、操作できるよう変数として持てるよう宣言します。

/** リストウィジェット */
TSharedPtr<SList> ListWidget;

レイアウトを以下のように記述します。

TSharedRef<SDockTab> FTestWindowModule::OnSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
{
    return 
        SNew(SDockTab)
        .TabRole(ETabRole::NomadTab)
        [
            SNew( SVerticalBox )

            + SVerticalBox::Slot()
            .Padding( FMargin( 5.f, 5.f, 5.f, 5.f ) )
            [
                // リスト
                SAssignNew( ListWidget, SList )
            ]
            
            + SVerticalBox::Slot()
            .Padding( FMargin( 5.f, 5.f, 5.f, 5.f ) )
            .AutoHeight()
            [
                // 要素追加ボタン
                SNew( SButton )
                .HAlign( HAlign_Center )
                .Text( LOCTEXT( "AddButtonText", "要素を追加!" ) )
                .OnClicked_Raw( this, &FTestWindowModule::OnClickedAddButton )
            ]

            + SVerticalBox::Slot()
            .Padding( FMargin( 5.f, 5.f, 5.f, 5.f ) )
            .AutoHeight()
            [
                // リストクリアボタン
                SNew( SButton )
                .HAlign( HAlign_Center )
                .Text( LOCTEXT( "ClearButtonText", "リストをクリア!" ) )
                .OnClicked_Raw( this, &FTestWindowModule::OnClickedClearButton )
            ]
        ];
}

あとはボタンで要素の追加、リストのクリアを行えるようにします。

/**
 * 要素の追加ボタンが押されたときの処理
 */
FReply FTestWindowModule::OnClickedAddButton()
{
    ListWidget->AddItem( TEXT("ロジカルビート") );

    return FReply::Handled();
}

/**
 * リストのクリアボタンが押されたときの処理
 */
FReply FTestWindowModule::OnClickedClearButton()
{
    ListWidget->ClearList();

    return FReply::Handled();
}

 

実行結果は以下のようになります。
要素の追加ボタンが押されると、リストに文字列が順に追加されます。
slateui_list_0

SScrollBoxウィジェットを使用しているので、範囲を超えるとスクロールするようになります。
slateui_list_1

リストのクリアボタンが押されるとリストがクリアされました。
slateui_list_2

前回記事で紹介したインプットボックスと組み合わせて、入力された文字列を追加したり、
レイアウト編で紹介したSBorderウィジェットと組み合わせて、表のように表示したり、
エディター作成にはいろいろ活用できそうですね!
多用するレイアウトは独自ウィジェットとして作っておくと作業が楽になります。
是非参考にしてみてください!

 

 

【免責事項】
本サイトでの情報を利用することによる損害等に対し、株式会社ロジカルビートは一切の責任を負いません。