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 dlangui.graphics.gldrawbuf;
|
||||||
private import std.algorithm;
|
private import std.algorithm;
|
||||||
|
private import core.sync.mutex;
|
||||||
|
|
||||||
// specify debug=DebugMouseEvents for logging mouse handling
|
// specify debug=DebugMouseEvents for logging mouse handling
|
||||||
// specify debug=DebugRedraw for logging drawing and layouts handling
|
// specify debug=DebugRedraw for logging drawing and layouts handling
|
||||||
|
@ -44,6 +45,45 @@ enum WindowFlag : uint {
|
||||||
Modal = 4,
|
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.
|
* Window abstraction layer. Widgets can be shown only inside window.
|
||||||
|
@ -55,6 +95,8 @@ class Window {
|
||||||
protected uint _keyboardModifiers;
|
protected uint _keyboardModifiers;
|
||||||
protected uint _backgroundColor;
|
protected uint _backgroundColor;
|
||||||
protected Widget _mainWidget;
|
protected Widget _mainWidget;
|
||||||
|
protected EventList _eventList;
|
||||||
|
|
||||||
@property uint backgroundColor() const { return _backgroundColor; }
|
@property uint backgroundColor() const { return _backgroundColor; }
|
||||||
@property void backgroundColor(uint color) { _backgroundColor = color; }
|
@property void backgroundColor(uint color) { _backgroundColor = color; }
|
||||||
@property int width() const { return _dx; }
|
@property int width() const { return _dx; }
|
||||||
|
@ -163,6 +205,7 @@ class Window {
|
||||||
private long lastDrawTs;
|
private long lastDrawTs;
|
||||||
|
|
||||||
this() {
|
this() {
|
||||||
|
_eventList = new EventList();
|
||||||
_backgroundColor = 0xFFFFFF;
|
_backgroundColor = 0xFFFFFF;
|
||||||
}
|
}
|
||||||
~this() {
|
~this() {
|
||||||
|
@ -173,6 +216,8 @@ class Window {
|
||||||
destroy(_mainWidget);
|
destroy(_mainWidget);
|
||||||
_mainWidget = null;
|
_mainWidget = null;
|
||||||
}
|
}
|
||||||
|
destroy(_eventList);
|
||||||
|
_eventList = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animate(Widget root, long interval) {
|
private void animate(Widget root, long interval) {
|
||||||
|
@ -455,6 +500,64 @@ class Window {
|
||||||
return false;
|
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
|
/// dispatch mouse event to window content widgets
|
||||||
bool dispatchMouseEvent(MouseEvent event) {
|
bool dispatchMouseEvent(MouseEvent event) {
|
||||||
// ignore events if there is no root
|
// ignore events if there is no root
|
||||||
|
|
|
@ -152,6 +152,8 @@ version (USE_OPENGL) {
|
||||||
private __gshared bool DERELICT_GL3_RELOADED = false;
|
private __gshared bool DERELICT_GL3_RELOADED = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint CUSTOM_MESSAGE_ID = WM_USER + 1;
|
||||||
|
|
||||||
class Win32Window : Window {
|
class Win32Window : Window {
|
||||||
Win32Platform _platform;
|
Win32Platform _platform;
|
||||||
|
|
||||||
|
@ -309,6 +311,13 @@ class Win32Window : Window {
|
||||||
DestroyWindow(_hwnd);
|
DestroyWindow(_hwnd);
|
||||||
_hwnd = null;
|
_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() {
|
Win32ColorDrawBuf getDrawBuf() {
|
||||||
//RECT rect;
|
//RECT rect;
|
||||||
//GetClientRect(_hwnd, &rect);
|
//GetClientRect(_hwnd, &rect);
|
||||||
|
@ -984,6 +993,11 @@ LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
case CUSTOM_MESSAGE_ID:
|
||||||
|
if (window !is null) {
|
||||||
|
window.handlePostedEvent(cast(uint)lParam);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
case WM_ERASEBKGND:
|
case WM_ERASEBKGND:
|
||||||
// processed
|
// processed
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -954,6 +954,26 @@ class Widget {
|
||||||
return false;
|
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.
|
/// process mouse event; return true if event is processed by widget.
|
||||||
bool onMouseEvent(MouseEvent event) {
|
bool onMouseEvent(MouseEvent event) {
|
||||||
if (onMouseListener.assigned && onMouseListener(this, event))
|
if (onMouseListener.assigned && onMouseListener(this, event))
|
||||||
|
|
Loading…
Reference in New Issue