📄 calendardisplay.as
字号:
target.initializeFunction = fadeInTarget;
target.releaseFunction = fadeOutTarget;
target.x = 0;
target.y = _allDayAreaHeight - _scroller.scrollPosition;
target.unscaledWidth = unscaledWidth - _border.right;
target.unscaledHeight = 24 * _hourHeight;
target.alpha = 1;
// lay out the border that demarks the all-day event area.
target = _animator.targetFor(_allDayEventBorder);
target.x = _border.left+1;
target.y = _border.top + header.measuredHeight+1;
target.unscaledWidth = unscaledWidth - _border.left - _border.right - 1;
target.unscaledHeight = _allDayAreaHeight - target.y - 1;
// allright, time to lay out our intra-day events.
var daysEvents:Array = null;
// for each day
for(rPos=0;rPos < _rowLength;rPos++)
{
daysEvents = null;
// find the first event in our (sorted by start time) visible event list that starts
// _later_ than today.
for(i=0;i<events.length;i++)
{
if(events[i].start.getTime() >= endOfDay.getTime())
{
// we found one that doesn't start today; let's grab
// the ones that _do_ start today.
daysEvents = events.splice(0,i);
break;
}
}
// if we didn't find one that starts later than today,
// then _all_ of them start today.
if(daysEvents == null)
daysEvents = events;
// OK, we have a day and a set of events on that day...go lay them out.
layoutSingleDay(daysEvents,_border.left + rPos * _cellWidth,_allDayAreaHeight,_cellWidth,_cellHeight - header.measuredHeight);
// advance our markers to the next day.
startOfDay.date = startOfDay.date+1;
endOfDay.date = endOfDay.date+1;
}
}
// our layout routine that lays out the intra-day events in a single day.
private function layoutSingleDay(events:Array, cellLeft:Number,cellTop:Number,_cellWidth:Number,_cellHeight:Number):void
{
var openEvents:SortedArray = new SortedArray(null,"end");
var data:EventData;
var reservations:ReservationAgent = new ReservationAgent();
var pendingEvents:Array = [];
var maxOpenEvents:int = 0;
var renderer:UIComponent;
var i:int;
var target:LayoutTarget;
// we're going to loop until we've process all our events
while(1)
{
// if we're out of all events, we can stop.
if(events.length == 0 && openEvents.length == 0)
break;
// here's our basic strategy...loop forward, finding all the events that are open at the same time.
// assign each of those a slot. As events end, we free up their slot, and reassign them as new events
// start. So first, we need to loop forward, finding all the events that start before the next event
// ends
while(events.length > 0 && (openEvents.length == 0 || events[0].start.getTime() <= openEvents[0].end.getTime()))
{
// we need to consider this event.
var nextEvent:CalendarEvent = events.shift();
data = _eventData[nextEvent];
// assign an unused space to our event.
data.lane = reservations.reserve(nextEvent);
openEvents.addItem(nextEvent);
maxOpenEvents = Math.max(maxOpenEvents,openEvents.length);
}
// OK, we have a list of events that are open at the same time. Each open event has a slot assigned to it.
// Now we loop forward, closing out all events that close before the next event starts.
while(openEvents.length > 0 && (events.length == 0 || openEvents[0].end.getTime() < events[0].start.getTime()))
{
var closingEvent:CalendarEvent = openEvents.shift();
// we're going to need to do some postprocessing on these events, so let's add it to a running list.
pendingEvents.push(closingEvent);
// free up its slot for reuse.
reservations.release(closingEvent);
// if we have no open events left, then we've considered a full set of contiguous events. That means
// that for each of the events in this block, we have all the info we need to decide how to render it.
// so we'll do our postprocessing, and position each event.
if(openEvents.length == 0)
{
// we know the maximum number of events we'll have open at any one time...that tells us how
// wide each event will be.
var laneWidth:Number = _cellWidth/maxOpenEvents;
for(i=0;i<pendingEvents.length;i++)
{
data = _eventData[pendingEvents[i]];
renderer = data.renderers[0];
// make sure the renderer is in its expanded mode.
ICalendarEventRenderer(renderer).displayMode = "box";
// now lay out that single event. It's position
// is a combination of what hour it starts, what slot it got placed in,
// its duration, the scroll position of the calendar, and the borders.
target = layoutSingleEvent(data,renderer,
cellLeft + EVENT_INSET + data.lane * laneWidth,
-_scroller.scrollPosition + _hourHeight * ((data.range.start.getTime() - _tz.startOfDay(data.range.start).getTime()) / DateUtils.MILLI_IN_HOUR) + cellTop,
laneWidth - 2*EVENT_INSET,
_hourHeight * (data.range.end.getTime() - data.range.start.getTime()) / DateUtils.MILLI_IN_HOUR
);
target.animate = true;
renderer.visible = true;
}
// on to our next block of contiguous events.
maxOpenEvents = 0;
pendingEvents = [];
}
}
}
}
// our main layout routine that is responsible for rendering month view.
private function layoutMonth():void
{
var startOfDay:Date = dateForIndex(0);
var endOfDay:Date = dateForIndex(1);
var openEvents:SortedArray = new SortedArray(null,"end");
var reservations:ReservationAgent = new ReservationAgent();
var events:Array = _visibleEvents.concat();
var renderTop:int;
var data:EventData;
var renderer:UIComponent;
var target:LayoutTarget;
var rPos:int;
var cPos:int;
var i:int;
var openingEvents:Array;
var aboveBottom:Boolean;
// first layout the days and day headers.
layoutCells();
// since we're rendering full screen, and we're not doing any scrolling, we
// won't need a mask.
_eventLayer.mask = null;
_eventMask.visible = false;
// we won't do any scrolling, so let's hide our scrollbar.
target = _animator.releaseTarget(_scroller);
if(target != null)
target.animate = false;
// month view doesn't have any gridlines, so hide the gridlines.
target = _animator.releaseTarget(_hourGrid);
if(target != null)
target.animate = false;
// same things for our all-day event border.
target = _animator.releaseTarget(_allDayEventBorder);
if(target != null)
target.animate = false;
// alright, now we're going to lay out day by day. So for each row, for each column...
for(cPos = 0;cPos<_columnLength;cPos++)
{
for(rPos=0;rPos < _rowLength;rPos++)
{
// the index of the current day in our visible range.
var index:int = rPos + cPos*_rowLength;
// the header for this day.
var header:UIComponent = _headerCache.instances[index];
// look at our list of open events. If any of these events
// have ended before today....
while (openEvents.length > 0 && openEvents[0].end < startOfDay)
{
reservations.release(openEvents.shift());
}
// if there are still events we haven't processed yet...
if(events.length > 0)
{
openingEvents = [];
// find all the events from our (start time sorted) list that start _before_ today.
// note that since we're trimming this list as we go, this really means all the events
// that start _today_.
while(events.length > 0 && events[0].start.getTime() < endOfDay.getTime())
{
data = _eventData[events.shift()];
openEvents.addItem(data.event);
openingEvents.push(data);
}
//we're going to lay out these events vertically. So start from the bottom of the header.
renderTop = header.measuredHeight;
// for each opening event...
for(i=0;i<openingEvents.length;i++)
{
data = openingEvents[i];
// assign it a slot.
var reservation:int = reservations.reserve(data.event);
// now if the event only intersects with a single week
if(_tz.rangeWeekSpan(data.range) == 1)
{
// it should only have a single renderer
renderer = data.renderers[0];
// put it into its compact line mode.
ICalendarEventRenderer(renderer).displayMode = "line";
// and lay out that one single renderer to match the length of the event
// (at least, the length of its intersection with the visible range)
target = layoutSingleEvent(data,renderer,
_border.left + rPos * _cellWidth + EVENT_INSET,
_border.top + cPos * _cellHeight + renderTop + renderer.measuredHeight * reservation,
_cellWidth * Math.max(1,_tz.rangeDaySpan(data.range)) - 2*EVENT_INSET,
renderer.measuredHeight
);
// if this event is below the bottom of the day it is contained in,
// we want to hide it.
aboveBottom = (target.y + target.unscaledHeight <= _border.top + (cPos+1) * _cellHeight)
target.animate = aboveBottom;
renderer.visible = aboveBottom;
}
else
{
// this event intersects with multiple weeks. That means it needs to span
// multiple rows. So it should have N renderers associated with it,
// one for each week it intersects. We need to lay each one out appropriately.
var weekSpan:int = _tz.rangeWeekSpan(data.range);
// how many days this event (or its intersection with the visible range)
// spans.
var daysRemaining:int = _tz.rangeDaySpan(data.range);
// where the renderer should start. For our first renderer, it will start on the first
// day of the event. All remaining renderers will start on sunday.
var rendererStart:int = rPos;
// for each week it intersects
for(var j:int = 0;j<weekSpan;j++)
{
// grab a renderer assigned to it.
renderer = data.renderers[j];
if(renderer != null){
// put it in compact mode.
ICalendarEventRenderer(renderer).displayMode = "line";
// figure out how long it should be, based on its start position, and
// the length of the event.
var currentDaySpan:int = Math.min(daysRemaining, 7 - rendererStart);
// and position it.
target = layoutSingleEvent(data,renderer,
_border.left + rendererStart * _cellWidth + EVENT_INSET,
_border.top + (cPos + j) * _cellHeight + renderTop + renderer.measuredHeight * reservation,
_cellWidth * currentDaySpan - 2*EVENT_INSET,
renderer.measuredHeight
);
// again, if we have so many events that this event ends up below the bottom of the day,
// we'll hide it.
aboveBottom = (target.y + target.unscaledHeight <= _border.top + (cPos+j+1) * _cellHeight)
target.animate = aboveBottom;
renderer.visible = aboveBottom;
// keep track of how many days remain to be rendererd for this event.
daysRemaining -= currentDaySpan;
// and make a note of the fact that for the next week, we want the renderer to start
// on day 0...sunday.
rendererStart = 0;
}
}
}
}
}
// advance to our next day.
startOfDay.date = startOfDay.date+1;
endOfDay.date = endOfDay.date+1;
}
}
}
// utility function for laying out a single day.
private function layoutSingleEvent(eventData:EventData, renderer:UIComponent,x:Number,y:Number,w:Number,h:Number):LayoutTarget
{
// grab our LayoutTarget object for this event renderer.
var target:LayoutTarget = _animator.targetFor(renderer);
// to make our lives easier, we don't bother trying to deal with tracking which events have been renderered before, and which
// ones haven't. We just give the layoutTarget an init function, and if the layout animator has never seen it before,
// it calls the init function for us.
target.initializeFunction = setupNewEventTarget;
target.alpha = 1;
// we'll render events that are currently the target of a drag operation a little differently
if(_dragEventData == eventData)
{
// if it's being moved, we want it to have a nice transparent look to indicate that its position is temporary.
if (_dragType != DragType.RESIZE)
{
target.alpha = .5;
}
}
target.x = x;
target.y = y;
target.unscaledHeight = h;
target.unscaledWidth = w
return target;
}
//----------------------------------------------------------------------------------------------------
// scrolling
//----------------------------------------------------------------------------------------------------
// callback for when the user scrolls with the scrollbar.
private function scrollHandler(event:ScrollEvent):void
{
_scrollHour = _scroller.scrollPosition / _hourHeight;
_animator.invalidateLayout(false);
// scrolling is live feedback, so we don't want to bother with animation.
_animator.updateLayoutWithoutAnimation();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -