diff --git a/dlanguilib.visualdproj b/dlanguilib.visualdproj
index 510b6bd6..8b86a837 100644
--- a/dlanguilib.visualdproj
+++ b/dlanguilib.visualdproj
@@ -66,7 +66,7 @@
0
DebugFocus
0
- EmbedStandardResources Unicode USE_SDL USE_FREETYPE
+ EmbedStandardResources Unicode
0
0
1
diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d
index 089ea51f..9fbb5949 100644
--- a/src/dlangui/platforms/common/platform.d
+++ b/src/dlangui/platforms/common/platform.d
@@ -117,20 +117,39 @@ class TimerInfo {
@property long nextTimestamp() { return _nextTimestamp; }
/// widget to route timer event to
@property Widget targetWidget() { return _targetWidget; }
+ /// return true if timer is not yet cancelled
+ @property bool valid() { return _targetWidget !is null; }
protected ulong _id;
protected long _interval;
protected long _nextTimestamp;
protected Widget _targetWidget;
-}
-class TimerQueue {
- protected TimerInfo[] _queue;
- void add(TimerInfo event) {
- int len = cast(int)_queue.length;
+ override bool opEquals(Object obj) {
+ TimerInfo b = cast(TimerInfo)obj;
+ if (!b)
+ return false;
+ return b._nextTimestamp == _nextTimestamp;
+ }
+ override int opCmp(Object obj) {
+ TimerInfo b = cast(TimerInfo)obj;
+ if (!b)
+ return false;
+ if (valid && !b.valid)
+ return -1;
+ if (!valid && b.valid)
+ return 1;
+ if (!valid && !b.valid)
+ return 0;
+ if (_nextTimestamp < b._nextTimestamp)
+ return -1;
+ if (_nextTimestamp > b._nextTimestamp)
+ return 1;
+ return 0;
}
}
+
/**
* Window abstraction layer. Widgets can be shown only inside window.
*
@@ -261,6 +280,7 @@ class Window {
this() {
_eventList = new EventList();
+ _timerQueue = new TimerQueue();
_backgroundColor = 0xFFFFFF;
}
~this() {
@@ -272,6 +292,7 @@ class Window {
_mainWidget = null;
}
destroy(_eventList);
+ destroy(_timerQueue);
_eventList = null;
}
@@ -818,6 +839,124 @@ class Window {
dlg.show();
}
+ protected TimerQueue _timerQueue;
+
+
+ /// schedule timer for interval in milliseconds - call window.onTimer when finished
+ protected void scheduleSystemTimer(long intervalMillis) {
+ Log.d("override scheduleSystemTimer to support timers");
+ }
+
+ /// system timer interval expired - notify queue
+ protected void onTimer() {
+ bool res = _timerQueue.notify();
+ if (res) {
+ // check if update needed and redraw if so
+ update(false);
+ }
+ long nextInterval = _timerQueue.nextIntervalMillis();
+ if (nextInterval > 0) {
+ scheduleSystemTimer(nextInterval);
+ }
+ }
+
+ /// set timer for destination widget - destination.onTimer() will be called after interval expiration; returns timer id
+ ulong setTimer(Widget destination, long intervalMillis) {
+ if (!isChild(destination)) {
+ Log.e("setTimer() is called not for child widget of window");
+ return 0;
+ }
+ ulong res = _timerQueue.add(destination, intervalMillis);
+ long nextInterval = _timerQueue.nextIntervalMillis();
+ if (nextInterval > 0) {
+ scheduleSystemTimer(intervalMillis);
+ }
+ return res;
+ }
+
+ /// cancel previously scheduled widget timer (for timerId pass value returned from setTimer)
+ void cancelTimer(ulong timerId) {
+ _timerQueue.cancelTimer(timerId);
+ }
+
+ /// timers queue
+ private class TimerQueue {
+ protected TimerInfo[] _queue;
+ /// add new timer
+ ulong add(Widget destination, long intervalMillis) {
+ TimerInfo item = new TimerInfo(destination, intervalMillis);
+ _queue ~= item;
+ sort(_queue);
+ return item.id;
+ }
+ /// cancel timer
+ void cancelTimer(ulong timerId) {
+ for (size_t i = _queue.length - 1; i >= 0; i--) {
+ if (_queue[i].id == timerId) {
+ _queue[i].cancel();
+ break;
+ }
+ }
+ }
+ /// returns interval if millis of next scheduled event or -1 if no events queued
+ long nextIntervalMillis() {
+ if (!_queue.length || !_queue[0].valid)
+ return -1;
+ long delta = _queue[0].nextTimestamp - currentTimeMillis;
+ if (delta < 1)
+ delta = 1;
+ return delta;
+ }
+ private void cleanup() {
+ sort(_queue);
+ size_t newsize = 0;
+ for (size_t i = _queue.length - 1; i >= 0; i--) {
+ if (!_queue[i].valid) {
+ newsize = i;
+ }
+ }
+ if (_queue.length > newsize)
+ _queue.length = newsize;
+ }
+ private TimerInfo[] expired() {
+ long ts = currentTimeMillis;
+ TimerInfo[] res;
+ for (int i = 0; i < _queue.length; i++) {
+ if (_queue[i].nextTimestamp <= ts)
+ res ~= _queue[i];
+ }
+ return res;
+ }
+ /// returns true if at least one widget was notified
+ bool notify() {
+ bool res = false;
+ checkValidWidgets();
+ TimerInfo[] list = expired();
+ if (list) {
+ for (int i = 0; i < list.length; i++) {
+ Widget w = _queue[i].targetWidget;
+ if (w && !isChild(w))
+ _queue[i].cancel();
+ else {
+ _queue[i].notify();
+ res = true;
+ }
+ }
+ }
+ cleanup();
+ return res;
+ }
+ private void checkValidWidgets() {
+ for (int i = 0; i < _queue.length; i++) {
+ Widget w = _queue[i].targetWidget;
+ if (w && !isChild(w))
+ _queue[i].cancel();
+ }
+ cleanup();
+ }
+ }
+
+
}
/**
diff --git a/src/dlangui/platforms/windows/winapp.d b/src/dlangui/platforms/windows/winapp.d
index 07924e44..0f1448d0 100644
--- a/src/dlangui/platforms/windows/winapp.d
+++ b/src/dlangui/platforms/windows/winapp.d
@@ -318,6 +318,27 @@ class Win32Window : Window {
PostMessageW(_hwnd, CUSTOM_MESSAGE_ID, 0, event.uniqueId);
}
+ private long _nextExpectedTimerTs;
+ private UINT_PTR _timerId;
+
+ /// schedule timer for interval in milliseconds - call window.onTimer when finished
+ override protected void scheduleSystemTimer(long intervalMillis) {
+ if (intervalMillis < 10)
+ intervalMillis = 10;
+ long nextts = currentTimeMillis + intervalMillis;
+ if (_timerId && _nextExpectedTimerTs && _nextExpectedTimerTs < nextts + 10)
+ return; // don't reschedule timer, timer event will be received soon
+ if (_hwnd) {
+ _timerId = SetTimer(_hwnd, _timerId, cast(uint)intervalMillis, null);
+ _nextExpectedTimerTs = nextts;
+ }
+ }
+
+ void handleTimer(UINT_PTR timerId) {
+ onTimer();
+ }
+
+
Win32ColorDrawBuf getDrawBuf() {
//RECT rect;
//GetClientRect(_hwnd, &rect);
@@ -1062,6 +1083,12 @@ LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
return 1;
}
break;
+ case WM_TIMER:
+ if (window !is null) {
+ window.handleTimer(wParam);
+ return 0;
+ }
+ break;
case WM_GETMINMAXINFO:
case WM_NCCREATE:
case WM_NCCALCSIZE:
diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d
index 920b7b2b..db9b5661 100644
--- a/src/dlangui/widgets/widget.d
+++ b/src/dlangui/widgets/widget.d
@@ -923,9 +923,21 @@ class Widget {
return res;
}
+
+ /// set new timer to call onTimer() after specified interval (for recurred notifications, return true from onTimer)
+ ulong setTimer(long intervalMillis) {
+ return window.setTimer(this, intervalMillis);
+ }
+
+ /// cancel timer - pass value returned from setTimer() as timerId parameter
+ void cancelTimer(ulong timerId) {
+ window.cancelTimer(timerId);
+ }
+
/// handle timer; return true to repeat timer event after next interval, false cancel timer
bool onTimer(ulong id) {
// override to do something useful
+ // return true to repeat after the same interval, false to stop timer
return false;
}