mirror of https://github.com/buggins/dlangui.git
support custom events and execution of tasks in GUI thread - win32 implementation
This commit is contained in:
parent
ec0f7ea8f4
commit
414563de0c
|
@ -1014,3 +1014,54 @@ string keyName(uint keyCode) {
|
|||
}
|
||||
}
|
||||
|
||||
/// base class for custom events
|
||||
class CustomEvent {
|
||||
protected int _id;
|
||||
protected uint _uniqueId;
|
||||
|
||||
protected static uint _uniqueIdGenerator;
|
||||
|
||||
protected Widget _destinationWidget;
|
||||
// event id
|
||||
@property int id() { return _id; }
|
||||
@property uint uniqueId() { return _uniqueId; }
|
||||
@property Widget destinationWidget() { return _destinationWidget; }
|
||||
|
||||
protected Object _objectParam;
|
||||
@property Object objectParam() {
|
||||
return _objectParam;
|
||||
}
|
||||
@property CustomEvent objectParam(Object value) {
|
||||
_objectParam = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected int _intParam;
|
||||
@property int intParam() {
|
||||
return _intParam;
|
||||
}
|
||||
@property CustomEvent intParam(int value) {
|
||||
_intParam = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
this(int ID) {
|
||||
_id = ID;
|
||||
_uniqueId = ++_uniqueIdGenerator;
|
||||
}
|
||||
}
|
||||
|
||||
immutable int CUSTOM_RUNNABLE = 1;
|
||||
|
||||
/// operation to execute (usually sent from background threads to run some code in UI thread)
|
||||
class RunnableEvent : CustomEvent {
|
||||
protected void delegate() _action;
|
||||
this(int ID, Widget destinationWidget, void delegate() action) {
|
||||
super(ID);
|
||||
_destinationWidget = destinationWidget;
|
||||
_action = action;
|
||||
}
|
||||
void run() {
|
||||
_action();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import dlangui.dialogs.msgbox;
|
|||
|
||||
private import dlangui.graphics.gldrawbuf;
|
||||
private import std.algorithm;
|
||||
private import core.sync.mutex;
|
||||
|
||||
// specify debug=DebugMouseEvents for logging mouse handling
|
||||
// specify debug=DebugRedraw for logging drawing and layouts handling
|
||||
|
@ -44,6 +45,45 @@ enum WindowFlag : uint {
|
|||
Modal = 4,
|
||||
}
|
||||
|
||||
/// protected event list
|
||||
/// references to posted messages can be stored here at least to keep live reference and avoid GC
|
||||
/// as well, on some platforms it's easy to send id to message queue, but not pointer
|
||||
class EventList {
|
||||
protected Mutex _mutex;
|
||||
protected Collection!CustomEvent _events;
|
||||
this() {
|
||||
_mutex = new Mutex();
|
||||
}
|
||||
~this() {
|
||||
destroy(_mutex);
|
||||
_mutex = null;
|
||||
}
|
||||
/// puts event into queue, returns event's unique id
|
||||
long put(CustomEvent event) {
|
||||
_mutex.lock();
|
||||
scope(exit) _mutex.unlock();
|
||||
_events.pushBack(event);
|
||||
return event.uniqueId;
|
||||
}
|
||||
/// return next event
|
||||
CustomEvent get() {
|
||||
_mutex.lock();
|
||||
scope(exit) _mutex.unlock();
|
||||
return _events.popFront();
|
||||
}
|
||||
/// return event by unique id
|
||||
CustomEvent get(uint uniqueId) {
|
||||
_mutex.lock();
|
||||
scope(exit) _mutex.unlock();
|
||||
for (int i = 0; i < _events.length; i++) {
|
||||
if (_events[i].uniqueId == uniqueId) {
|
||||
return _events.remove(i);
|
||||
}
|
||||
}
|
||||
// not found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Window abstraction layer. Widgets can be shown only inside window.
|
||||
|
@ -55,6 +95,8 @@ class Window {
|
|||
protected uint _keyboardModifiers;
|
||||
protected uint _backgroundColor;
|
||||
protected Widget _mainWidget;
|
||||
protected EventList _eventList;
|
||||
|
||||
@property uint backgroundColor() const { return _backgroundColor; }
|
||||
@property void backgroundColor(uint color) { _backgroundColor = color; }
|
||||
@property int width() const { return _dx; }
|
||||
|
@ -163,6 +205,7 @@ class Window {
|
|||
private long lastDrawTs;
|
||||
|
||||
this() {
|
||||
_eventList = new EventList();
|
||||
_backgroundColor = 0xFFFFFF;
|
||||
}
|
||||
~this() {
|
||||
|
@ -173,6 +216,8 @@ class Window {
|
|||
destroy(_mainWidget);
|
||||
_mainWidget = null;
|
||||
}
|
||||
destroy(_eventList);
|
||||
_eventList = null;
|
||||
}
|
||||
|
||||
private void animate(Widget root, long interval) {
|
||||
|
@ -455,6 +500,64 @@ class Window {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// post event to handle in UI thread (this method can be used from background thread)
|
||||
void postEvent(CustomEvent event) {
|
||||
// override to post event into window message queue
|
||||
_eventList.put(event);
|
||||
}
|
||||
|
||||
/// post task to execute in UI thread (this method can be used from background thread)
|
||||
void executeInUiThread(void delegate() runnable) {
|
||||
RunnableEvent event = new RunnableEvent(CUSTOM_RUNNABLE, null, runnable);
|
||||
postEvent(event);
|
||||
}
|
||||
|
||||
/// remove event from queue by unique id if not yet dispatched (this method can be used from background thread)
|
||||
void cancelEvent(uint uniqueId) {
|
||||
CustomEvent ev = _eventList.get(uniqueId);
|
||||
if (ev) {
|
||||
//destroy(ev);
|
||||
}
|
||||
}
|
||||
|
||||
/// remove event from queue by unique id if not yet dispatched and dispatch it
|
||||
void handlePostedEvent(uint uniqueId) {
|
||||
CustomEvent ev = _eventList.get(uniqueId);
|
||||
if (ev) {
|
||||
dispatchCustomEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
/// handle all events from queue, if any (call from UI thread only)
|
||||
void handlePostedEvents() {
|
||||
for(;;) {
|
||||
CustomEvent e = _eventList.get();
|
||||
if (!e)
|
||||
break;
|
||||
dispatchCustomEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// dispatch custom event
|
||||
bool dispatchCustomEvent(CustomEvent event) {
|
||||
if (event.destinationWidget) {
|
||||
if (!isChild(event.destinationWidget)) {
|
||||
//Event is sent to widget which does not exist anymore
|
||||
return false;
|
||||
}
|
||||
return event.destinationWidget.onEvent(event);
|
||||
} else {
|
||||
// no destination widget
|
||||
RunnableEvent runnable = cast(RunnableEvent)event;
|
||||
if (runnable) {
|
||||
// handle runnable
|
||||
runnable.run();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// dispatch mouse event to window content widgets
|
||||
bool dispatchMouseEvent(MouseEvent event) {
|
||||
// ignore events if there is no root
|
||||
|
|
|
@ -152,6 +152,8 @@ version (USE_OPENGL) {
|
|||
private __gshared bool DERELICT_GL3_RELOADED = false;
|
||||
}
|
||||
|
||||
const uint CUSTOM_MESSAGE_ID = WM_USER + 1;
|
||||
|
||||
class Win32Window : Window {
|
||||
Win32Platform _platform;
|
||||
|
||||
|
@ -309,6 +311,13 @@ class Win32Window : Window {
|
|||
DestroyWindow(_hwnd);
|
||||
_hwnd = null;
|
||||
}
|
||||
|
||||
/// post event to handle in UI thread (this method can be used from background thread)
|
||||
override void postEvent(CustomEvent event) {
|
||||
super.postEvent(event);
|
||||
PostMessageW(_hwnd, CUSTOM_MESSAGE_ID, 0, event.uniqueId);
|
||||
}
|
||||
|
||||
Win32ColorDrawBuf getDrawBuf() {
|
||||
//RECT rect;
|
||||
//GetClientRect(_hwnd, &rect);
|
||||
|
@ -984,6 +993,11 @@ LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||
}
|
||||
}
|
||||
return 0;
|
||||
case CUSTOM_MESSAGE_ID:
|
||||
if (window !is null) {
|
||||
window.handlePostedEvent(cast(uint)lParam);
|
||||
}
|
||||
return 1;
|
||||
case WM_ERASEBKGND:
|
||||
// processed
|
||||
return 1;
|
||||
|
|
|
@ -954,6 +954,26 @@ class Widget {
|
|||
return false;
|
||||
}
|
||||
|
||||
/// handle custom event
|
||||
bool onEvent(CustomEvent event) {
|
||||
RunnableEvent runnable = cast(RunnableEvent)event;
|
||||
if (runnable) {
|
||||
// handle runnable
|
||||
runnable.run();
|
||||
return true;
|
||||
}
|
||||
// override to handle more events
|
||||
return false;
|
||||
}
|
||||
|
||||
/// execute delegate later in UI thread if this widget will be still available (can be used to modify UI from background thread, or just to postpone execution of action)
|
||||
void executeInUiThread(void delegate() runnable) {
|
||||
if (!window)
|
||||
return;
|
||||
RunnableEvent event = new RunnableEvent(CUSTOM_RUNNABLE, this, runnable);
|
||||
window.postEvent(event);
|
||||
}
|
||||
|
||||
/// process mouse event; return true if event is processed by widget.
|
||||
bool onMouseEvent(MouseEvent event) {
|
||||
if (onMouseListener.assigned && onMouseListener(this, event))
|
||||
|
|
Loading…
Reference in New Issue