UIEventHandler の登録 API を整理する
lriki opened this issue · 0 comments
[2021/1/6] やっぱり現状維持にしてみる
Builder パターンの導入を始めたので、そっちで対応してみる。C++ から使うときはほとんどこのケースで足りる。
auto button = UIButton::Builder()
.onClicked(handler)
.build();
新しめの UI フレームワークは Builder パターンないしそれに近い、副作用の少ない方法で UI要素を構築するケースが多い。これで十分な感じ。
connectXX は主に Binding を作るとき、「これはイベントハンドラの登録関数です」を強調するのに使うことにしてみる。
イベント名は過去形?
ネイティブに近いフレームワークは過去形が多い傾向。
ただ過去形に統一しすぎると MouseDowned とかなじみ無い名前になる。
タイミングを区別するなら WPF の "Preview" Prefix のようなものを付ける、でいいかも。
Proposal
connectOnXXXX 及び onXXXX のインターフェイスを変更する。
例えば UIButton の Clicked イベントであれば、
Ref<EventConnection> connectOnClicked(Ref<UIEventHandler> handler);
virtual void onClicked(UIEventArgs* e) override;
↓
UIButton* onClicked(Ref<UIEventHandler> handler, Ref<EventConnection>* outConnection = nullptr);
virtual void onClickedEvent(UIEventArgs* e) override;
ただし、on~ は他のモジュールでも使用している仮想関数実装の命名規則でもあるため、上記そのまま適用は難しいかもしれない。
候補としては、
- setOnClicked
- Ruby や C# binding で使うときに on_clicked や OnClicked となるため、他モジュールの命名規則と衝突する。
- setClickedHandler
- まだちょっと長いかも…。特にスクリプト系 Binding から見た時。
- setClicked
- 落としどころかも?通常プロパティと区別するため、イベント名には名詞を使わないように注意する必要がある。
Motivation
現在、ユーザープログラムから、イベントをハンドリングする方法は次の2通り。
- A. connectOnXXXX にコールバックを登録する
- B. コントロールを継承して仮想関数 onXXXX を実装する
ユーザープログラムから見ると、このうち A は非常によく使い、B はあまり使わない。
(A はコントロールを利用するための API であるが、B はコントロールをカスタマイズするための API)
そのため以下のような不満が出てきた。
- A のほうが頻繁に使うのにタイプ量が多い
- 戻り値が決まっているので Builder パターンのようなメソッドチェインが組めない
- EventConnection はほとんどのケースで使用しないが、そうすると VisualStudio でインテリセンスの警告が出る → C26444
Note
on~ を採用しているものはそれなりにある。ただ、signal ではなく普通のプロパティとして単一のハンドラをセットするケースが多い印象。
C# の Event や、signal-slot の仕組みで作られているものは on~ ではないことが多いみたい。
WPF
- MouseEnter += _handler
Material-UI
- onClick = _handler
onClick の呼び出し元は handleClick.
ただ仮想関数という考え方ではない。
一番根っこは div の onClick にローカル定義した関数をセットしているだけ。
<div ... onClick={handleClick} ...>
JavaScript
- onclick = _handler
- onmousedown = _handler
https://phpjavascriptroom.com/?t=js&p=event
Flutter
- onPressed: _handler
Unity(UIElements)
- clicked += _handler
※ onClick もあるが、Obsolete としてマークされている。また、コンストラクタでもハンドラを受け取れる。
これらはすべて C# の event として実装されている。
- Button.clicked
- ListView.onSelectionChanged
仮想関数は次のような命名になっている。
- Clickable.OnMouseDown()
- Clickable.OnMouseMove()
UE4
- (BP) On Clicked
- (Slate) OnClicked(_handler)
イベントと仮想関数の命名規則に区別はない。
- OnMouseButtonDown()
- OnPaint()
- OnClicked
- OnAcceptDrop
- OnPaintHandler
Kivy
- (.kv) on_press: _handler
- (.py) on_press=_handler (コンストラクタ名前付き引数)
- (.py) b.bind(on_press=anim_btn)
イベントと仮想関数の命名規則に区別はない。
- on_touch_down()
- on_touch_move()
- on_touch_up()
- on_press:
- on_release:
Qt
- (QWidgets) connect(a, SIGNAL(valueChanged()), b, SLOT(_handler));
- (QML) onClicked: _handler
Android
- setOnClickListener(_handler)
UIKit
- button.addTarget(self, action: #selector(_handler),
GTK
- g_signal_connect (button, "clicked", G_CALLBACK (_handler), (gpointer) "button");
Rust azul
let button = Button::with_label("+1").dom().with_id("button")
.with_callback(On::MouseUp, Callback(update_counter));
Rust gtk
button.connect_clicked(move |_| { ... });
Rust iced
Button::new(&mut self.reset_button, Text::new("reset"))
.on_press(Message::Reset),
OrbTk iced
self.on_mouse_up(move |p| { ... });
Note
現時点で Event<> な変数は次の通り。
canUndoChanged;
canRedoChanged;
m_onCollisionEnter;
m_onCollisionLeave;
m_onCollisionStay;
m_onUIEvent;
m_onChecked;
m_onUnchecked;
m_onActivated;
m_onDeactivated;
m_onSubmit;
DragStarted;
DragDelta;
DragCompleted;
DragCanceled;
m_onItemSubmitted;
m_onGenerateTreeItem;
m_onCanExecuteChanged;
m_onCanExecuteEvent;
m_onExecuteEvent;
m_onClosed;
m_onImGuiLayer;
m_onClick;
m_onItemClick;
m_onSelectionChanged;
m_onSelectedTabChanged;
m_onRender
onGenerateTreeItem, onRender あたりは他の名前にし辛いかも。
- (C++) setRender(...)
- (C#) Render += ...
- (Ruby) render = ...
onUIEvent は動詞でもないケースなので、Ruby とかだと普通の変数と区別ができなくなる。
スクリプト系だとこのあたりの視認性が悪くなるのはマイナス。