- Revision
- 16118
- Author
- jeffrey
- Date
- 2007-12-12 17:54:49 -0800 (Wed, 12 Dec 2007)
Log Message
[APP] Add lozenge dragging
- Drag lozenges smoothly in the x-axis, add a shadow where the event will
actually end up
- Enable dragging between canvases
- Create large transparent bitmaps in a more rational way
- Drag lozenges smoothly in the x-axis, add a shadow where the event will
actually end up
- Enable dragging between canvases
- Create large transparent bitmaps in a more rational way
Modified Paths
Diff
Modified: branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar_canvas.py (16117 => 16118)
--- branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar_canvas.py 2007-12-13 01:52:16 UTC (rev 16117) +++ branches/rearchitecture/Chandler-Platform/ocap/wxui/calendar_canvas.py 2007-12-13 01:54:49 UTC (rev 16118) @@ -75,6 +75,10 @@ DefaultColors = get_lozonge_colors('Blue', 'UnSelected') DefaultSelectedColors = get_lozonge_colors('Blue', 'Selected') +# arbitrarily chosen different constants that evaluate false +DRAG_NO_POSITION = None +DRAG_OTHER_CANVAS = False + class InitializeSlots(object): __slots__ = () def __init__(self, **kw): @@ -335,7 +339,6 @@ combined_dc.DrawBitmap(self._background_buffer, 0, 0) self.ShadeToday(combined_dc) - self.DrawPhantom(combined_dc) if self.draw_lozenges_from_cache: combined_dc.DrawBitmap(self._lozenge_buffer, 0, 0) @@ -347,6 +350,8 @@ combined_dc.DrawBitmap(self._lozenge_buffer, 0, 0) self.draw_lozenges_from_cache = True + self.DrawPhantom(combined_dc) + self.DrawDraggedLozenge(combined_dc) dc = wx.BufferedPaintDC(self) @@ -368,35 +373,62 @@ self.DrawLozenge(gc, event, rect, truncate, draw_text) def DrawDraggedLozenge(self, dc): - if not self.drag_id: + dragged_from = self.GetDraggedFromWidget() + if dragged_from and dragged_from != self: + if not self.drag_position: + return + depth, max_depth = 0, 0 + real_event = dragged_from.events.get(dragged_from.drag_id) + delta = timedelta(0) + elif not self.drag_id or self.drag_position == DRAG_OTHER_CANVAS: return + else: + depth, max_depth = self.EventDepth(self.drag_id) + real_event = self.events.get(self.drag_id) + delta = self.DragEndAdjust() + + offset = wx.Point2D() + gc = wx.GraphicsContext.Create(dc) - gc = wx.GraphicsContext.Create(dc) - real_event = self.events.get(self.drag_id) - drag_event = Event(id=None) - for slot in drag_event.__slots__: - if slot != 'id': - setattr(drag_event, slot, getattr(real_event, slot)) - delta = self.DragEndAdjust() - drag_time = (self.TimeFromPosition(self.drag_position) - if self.drag_position else None) - if self.drag_edge in (wx.TOP, wx.LEFT): - drag_start = drag_time if drag_time else real_event.start - drag_event.duration = max(delta, drag_event.end - drag_start) - max_start = real_event.end - delta - drag_event.start = min(drag_start, max_start) - elif self.drag_edge in (wx.BOTTOM, wx.RIGHT): - drag_end = drag_time if drag_time else real_event.end - duration = drag_end - drag_event.start + delta - drag_event.duration = max(delta, duration) - depth, max_depth = self.EventDepth(self.drag_id) + if self.drag_type == 'move' and self.drag_position == DRAG_NO_POSITION: + drag_event = real_event + else: + drag_event = Event(id=None) + for slot in drag_event.__slots__: + if slot != 'id': + setattr(drag_event, slot, getattr(real_event, slot)) + if self.drag_type == 'resize': + drag_time = (self.TimeFromPosition(self.drag_position) + if self.drag_position else None) + if self.drag_edge in (wx.TOP, wx.LEFT): + drag_start = drag_time if drag_time else real_event.start + drag_event.duration = max(delta, drag_event.end-drag_start) + max_start = real_event.end - delta + drag_event.start = min(drag_start, max_start) + elif self.drag_edge in (wx.BOTTOM, wx.RIGHT): + drag_end = drag_time if drag_time else real_event.end + duration = drag_end - drag_event.start + delta + drag_event.duration = max(delta, duration) + elif self.drag_position: + center = wx.Point2D(*self.drag_position) + center += self.drag_offset + drag_time = self.TimeFromPosition(center) + if drag_time: + depth, max_depth = 0, 0 + drag_event.start = drag_time - self.drag_time_offset + self.DrawPhantom(dc, drag_event.start, drag_event.end - delta) + offset = (center.x - self.DayColumnMiddle(center).x, + self.VerticalDragOffset()) + for interval in self.intervals: if drag_event.overlaps(interval): args = drag_event, interval, depth, max_depth rect, truncate, draw_text = self.LozengeGeometry(*args) + rect = wx.Rect2D(*rect) + rect.Offset(offset) self.DrawLozenge(gc, drag_event, rect, truncate, - draw_text, dragging=self.dragging) - + draw_text, True) + def EventDepth(self, event_id): """Return the depth, max_depth tuple for the event's first lozenge.""" event = self.events.get(event_id) @@ -451,6 +483,12 @@ day += timedelta(1) * int(x / BASE_DAY_WIDTH) return day + timedelta(hours=(y / self.hour_height)) + def DayColumnMiddle(self, position): + """Given a position in a day column, return the column's middle.""" + dummy_time = self.TimeFromPosition(position) + day_rect = self.RectFromTime(dummy_time, dummy_time) + return day_rect.GetCentre() + def SidesToTruncate(self, real_start, real_end, lozenge_start, lozenge_end): truncate = 0 if real_start >= lozenge_start else wx.TOP return truncate | (0 if real_end <= lozenge_end else wx.BOTTOM) @@ -591,14 +629,14 @@ def PhantomDepth(self, dt): return 0 - def DrawPhantom(self, dc): + def DrawPhantom(self, dc, start=None, end=None): """Highlight a rectangle for a not-yet-created event.""" - if not self.phantom_start: + start = start if start else self.phantom_start + end = end if end else self.phantom_end + if not start: return - start = self.phantom_start - end = self.phantom_end - if not end: - end = self.phantom_start + timedelta(hours=1) + + end = end if end else start + timedelta(hours=1) gc = wx.GraphicsContext.Create(dc) start, end = min(start, end), max(start, end) end += self.DragEndAdjust() @@ -607,8 +645,7 @@ if phantom.overlaps(interval): start = max(phantom.start, interval.start) end = min(phantom.end, interval.end) - rect = self.RectFromTime(start, end, - self.PhantomDepth(self.phantom_start)) + rect = self.RectFromTime(start, end, self.PhantomDepth(start)) self.ShadeRect(gc, rect, PHANTOM_COLOR) @@ -848,7 +885,9 @@ self.drag_edge = None self.dragging = False self.click_position = None - self.drag_position = None + self.drag_position = DRAG_NO_POSITION + self.drag_time_offset = timedelta(0) + self.drag_offset = wx.Point2D(0, 0) def OnMouse(self, event): threshold = 5 @@ -859,15 +898,20 @@ info = self.TopInfoFromPosition(position) self.phantom_end = None if info: - selected_id = info[0].id - self.drag_id = selected_id + ev, depth, max_depth, range, rect = info + selected_id = ev.id + self.drag_id = ev.id + self.drag_time_offset = self.TimeFromPosition(position) - ev.start + self.drag_offset.Set(self.DayColumnMiddle(position).x - + position.x, 0) self.phantom_start = None else: selected_id = None - self.phantom_start = roundTime(self.TimeFromPosition(position)) + start = self.TimeFromPosition(position) + if start: + self.phantom_start = roundTime(start, round_down=True) self.Refresh() wx.PostEvent(self, SelectItemsEvent(selected_ids=[selected_id])) - print "left down", self.TimeFromPosition(position) elif event.LeftUp(): if self.phantom_start: @@ -878,52 +922,66 @@ self.ResetDrag() self.Refresh() elif event.Entering() or event.Leaving(): - self.ResetDrag() - self.Refresh() - if event.Dragging(): + if self.drag_type != 'move': + self.ResetDrag() + self.Refresh() + elif event.Dragging(): if not self.click_position: - print "dragging, but nothing to do" + pass elif (not self.dragging and self.click_position.GetDistance(position) < threshold): - print "dragging within threshold" + pass elif not self.drag_type: + self.dragging = True edge = self.DragEdge(self.click_position) if edge: self.drag_type = 'resize' - ev, depth, max_depth, range, rect = self.TopInfoFromPosition(self.click_position) - self.drag_id = ev.id self.drag_edge = edge - self.dragging = True elif self.phantom_start: self.drag_type = 'phantom' - self.dragging = True - else: - if self.drag_type == 'resize': - self.drag_position = position - elif self.drag_type == 'phantom': - self.phantom_end = self.TimeFromPosition(position) else: - pass - #drag_and_drop.DoDragAndDrop(self, 'phantom_lozenge') - #print "done with DragAndDrop" - self.Refresh(keep_cache=True) + self.drag_type = 'move' + else: + if self.drag_type == 'move': + drag_and_drop.DoDragAndDrop(self) + self.ResetDrag() + self.Refresh() + else: + if self.drag_type == 'resize': + self.drag_position = position + elif self.drag_type == 'phantom': + self.phantom_end = self.TimeFromPosition(position) + self.Refresh(keep_cache=True) elif event.Moving(): self.UpdateCursor(position) event.Skip() + def VerticalDragOffset(self): + """Adjust lozenge position based on drag_position (used in all-day).""" + return 0 + def OnHover(self, x, y, drag_result): - if self.CurrentDragFormat() == 'phantom_lozenge': - if self.GetDraggedFromWidget() == self: - print "hovering in the right window" - return wx.DragMove - else: - print "hovering in the wrong window" - return wx.DragNone - return drag_result + """Hovering while drag and drop is in progress.""" + drag_position = wx.Point2D(*self.CalcUnscrolledPosition(x, y)) + if (not drag_position or not self.drag_position or + drag_position.GetRounded() != self.drag_position.GetRounded()): + self.drag_position = drag_position + self.Refresh(keep_cache=True) + dragged_from = self.GetDraggedFromWidget() + if dragged_from and dragged_from is not self: + if dragged_from.drag_position != DRAG_OTHER_CANVAS: + dragged_from.drag_position = DRAG_OTHER_CANVAS + dragged_from.Refresh(keep_cache=True) + wx.GetApp().Yield(True) + return wx.DragMove - def OnDragResize(self, x, y): - if not self.dragging: return + def OnLeave(self): + """Leaving while drag and drop is in progress.""" + self.drag_position = DRAG_NO_POSITION + if not self.dragging: + self.ResetDrag() + self.Refresh(keep_cache=True) def DragEndAdjust(self): """ @@ -991,7 +1049,7 @@ class AllDayCalendar(Calendar): draggable_edges = wx.LEFT, wx.RIGHT scroll=False - + def DrawBackground(self, gc): gc.SetPen(wx.Pen(MINOR_LINE_COLOR)) gc.StrokePath(self.GetDayLinePath(gc)) @@ -1031,17 +1089,19 @@ # to allow a rect to be calculated for the whole day, leave height # large if no depth is passed in if depth >= 0: - height = 2*TEXT_Y_MARGIN + self.title_font_height + 2*HALO_WIDTH - rect.y = height*depth - rect.height = height + rect.y = self.line_height*depth + rect.height = self.line_height return rect + @property + def line_height(self): + return 2*TEXT_Y_MARGIN + self.title_font_height + 2*HALO_WIDTH + def TimeFromPosition(self, position): dt = super(AllDayCalendar, self).TimeFromPosition(position) if dt: return dt.replace(hour=0, minute=0, second=0, microsecond=0) - def SidesToTruncate(self, real_start, real_end, lozenge_start, lozenge_end): truncate = 0 if real_start >= lozenge_start else wx.LEFT return truncate | (0 if real_end <= lozenge_end else wx.RIGHT) @@ -1062,6 +1122,15 @@ return len([e for e in cluster.event_list if event.overlaps(e)]) return 0 + def VerticalDragOffset(self): + """Adjust lozenge position based on drag_position (used in all-day).""" + if not self.drag_position: + return 0 + if not self.click_position: + return self.drag_position.y - self.line_height / 2 + return self.drag_position.y - (self.click_position.y % self.line_height) + + class CalendarPanel(wx.Panel): def __init__(self, parent, **kw): super(CalendarPanel, self).__init__(parent, -1, style=wx.NO_BORDER, **kw) @@ -1331,17 +1400,19 @@ gc.SetFont(font) return gc.GetTextExtent('M')[1] -empty = '\x00' * 1200**2 +empty = '\x00' * 500**2 def transparent_bitmap(w, h): image = wx.EmptyImage(w, h) image.InitAlpha() alpha = image.GetAlphaBuffer() - if len(alpha) < len(empty): - alpha[:] = empty[:len(alpha)] - else: - for i in xrange(len(alpha)): - alpha[i] = '\x00' + position = 0 + window = len(empty) + total = len(alpha) + while position < total: + end = min(total, position + window) + alpha[position:end] = empty[:end-position] + position += window return wx.BitmapFromImage(image) @@ -1350,11 +1421,12 @@ rect.Inset(-outset,-outset) return rect -def roundTime(dt, nearest_minutes=15): +def roundTime(dt, nearest_minutes=15, round_down=False): day_start = dt.combine(dt, time(0)) delta = dt - day_start nearest_seconds = 60.0 * nearest_minutes - seconds = round(delta.seconds / nearest_seconds) * nearest_seconds + round_func = int if round_down else round + seconds = round_func(delta.seconds / nearest_seconds) * nearest_seconds return day_start + timedelta(seconds=seconds) def shortTZ(dt, tzinfo=None):
Modified: branches/rearchitecture/Chandler-Platform/ocap/wxui/drag_and_drop.py (16117 => 16118)
--- branches/rearchitecture/Chandler-Platform/ocap/wxui/drag_and_drop.py 2007-12-13 01:52:16 UTC (rev 16117) +++ branches/rearchitecture/Chandler-Platform/ocap/wxui/drag_and_drop.py 2007-12-13 01:54:49 UTC (rev 16118) @@ -6,7 +6,8 @@ # what the drag source window was. DraggedFromWidget = None -def DoDragAndDrop(source_window, format_label, data_object=None): +def DoDragAndDrop(source_window, format_label='chandler_item_ids', + data_object=None): """ Do a Drag And Drop operation, given the data in the selection. @@ -42,7 +43,6 @@ """ Override this to decide whether or not to accept a dropped item. """ - print "OnRequestDrop", x, y, self.GetDraggedFromWidget() # default - don't allow drop onto ourself return self.GetDraggedFromWidget() is not self @@ -66,7 +66,6 @@ @return: A wxDragResult other than dragResult if you want to change the drag operation """ - #print "OnHover", dragResult, x, y, self.GetDraggedFromWidget() # default - don't allow dragging to ourself if self.GetDraggedFromWidget() is self: return wx.DragNone @@ -84,7 +83,10 @@ return DraggedFromWidget def CurrentDragFormat(self): - return self.GetDraggedFromWidget()._drag_format_label + if DraggedFromWidget: + return DraggedFromWidget._drag_format_label + else: + return "" class CustomDropTarget(wx.DropTarget): """
_______________________________________________ Commits mailing list [email protected] http://lists.osafoundation.org/mailman/listinfo/commits
