timers support part 1 - for win32 platform

This commit is contained in:
Vadim Lopatin 2015-01-28 15:14:32 +03:00
parent 016a958a17
commit 1377556d92
4 changed files with 184 additions and 6 deletions

View File

@ -66,7 +66,7 @@
<debuglevel>0</debuglevel>
<debugids>DebugFocus</debugids>
<versionlevel>0</versionlevel>
<versionids>EmbedStandardResources Unicode USE_SDL USE_FREETYPE</versionids>
<versionids>EmbedStandardResources Unicode</versionids>
<dump_source>0</dump_source>
<mapverbosity>0</mapverbosity>
<createImplib>1</createImplib>

View File

@ -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();
}
}
}
/**

View File

@ -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:

View File

@ -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;
}