[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r8331 - in trunk/gnue-designer/src: forms/PagePainter uidrivers/w
From: |
jcater |
Subject: |
[gnue] r8331 - in trunk/gnue-designer/src: forms/PagePainter uidrivers/wx/uihelpers/documentcanvas |
Date: |
Mon, 3 Apr 2006 18:24:41 -0500 (CDT) |
Author: jcater
Date: 2006-03-31 17:46:55 -0600 (Fri, 31 Mar 2006)
New Revision: 8331
Modified:
trunk/gnue-designer/src/forms/PagePainter/PagePainter.py
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/colors.py
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
Log:
More work on the new document painter canvas; added rubberband selection
support and reworked the mouse functions completely. Also implemented resizer
grippers.
Modified: trunk/gnue-designer/src/forms/PagePainter/PagePainter.py
===================================================================
--- trunk/gnue-designer/src/forms/PagePainter/PagePainter.py 2006-03-31
22:42:13 UTC (rev 8330)
+++ trunk/gnue-designer/src/forms/PagePainter/PagePainter.py 2006-03-31
23:46:55 UTC (rev 8331)
@@ -53,7 +53,7 @@
def init(self, object):
- canvas = self.canvas = DocumentCanvas(self)
+ canvas = self.canvas = PainterCanvas(self)
self.document.app.ui.autoSizer(self, canvas)
self.object = object
@@ -79,7 +79,11 @@
xscale = max(xscale, w)
yscale = max(yscale, h)
- canvas.set_grid_size(xscale, yscale)
+ # Make the scale slightly larger, so that input widgets
+ # are bigger than text labels.
+ xscale += 4
+ yscale += 4
+ canvas.set_grid_scale(xscale, yscale)
# Draw initial objects
self.object.walk(self.inventoryObject)
@@ -87,25 +91,28 @@
def inventoryObject(self, object):
- canvas = self.canvas
+ # Right now, we assume if an object has an x,y component, it is drawable.
+ # This won't be true when we support layout management.
try:
x = object['Char:x']
y = object['Char:y']
except:
+ print "Not inventorying %s" % object
return
- try:
- w = object['Char:width']
- h = object['Char:height']
- except:
- w = object['Char:width'] = 10
- h = object['Char:height'] = 1
+ canvas = self.canvas
- widget = MyObject(object, canvas)
+ cls = getWidgetSkinClass(object)
+
+ widget = cls(object, canvas)
canvas.add(widget)
- widget.SetSize(w*xscale,h*yscale)
- widget.MoveTo(x*xscale,y*yscale)
+ # Debugging...
+ widget.set_selected(True)
+
+ print "Added %s" % object._type
+
+
def __objectSelected(self, object):
pass
@@ -134,67 +141,139 @@
from gnue.designer.uidrivers.wx.uihelpers.documentcanvas.colors import
colorIndex
#--------------------------------------------------------------------------
-# MyObject
+# form widgets
#--------------------------------------------------------------------------
-class MyObject(DocumentWidget):
- def __init__(self, object, canvas):
- self.__object = object
- DocumentWidget.__init__(self, canvas)
-
- def draw(self, dc):
- dc.BeginDrawing()
- dc.SetPen(wx.Pen(colorIndex['text']))
- object = self.__object
- x,y,w,h = self.bounds
- dc.SetFont(wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT))
- if object._type == 'GFLabel':
- x = 0
- for char in object.text:
- dc.DrawText(char, x,0)
- x += xscale
- elif object._type == 'GFEntry':
- dc.DrawRoundedRectangle(0,0,w,h, 1)
+def getWidgetSkinClass(object):
+ if object._type == 'GFLabel':
+ cls = LabelWidget
+ elif object._type == 'GFEntry':
+ cls = EntryWidget
else:
- dc.DrawRoundedRectangle(0,0,w,h, 4)
+ cls = UnknownWidget
- def DragHitTest (self, x, y):
- if self.bounds.x <= x <= self.bounds.x + self.bounds.width and \
- self.bounds.y <= y <= self.bounds.y + self.bounds.height:
- return True
- else:
- return False
+ return cls
- def ConvertDrawableObjectToDataObject (self, x, y):
- object = self
- data = [ { "Type" : "selectedWidgets",
- "Attributes": {
- 'startingX': x,
- 'startingY': y,
- }
- }]
-# "name": dname,
-# "table": object.name,
-# "database": self.connectionName } }
-#
-#
- do =
wx.CustomDataObject(wx.CustomDataFormat("application/x-gnue-designer"))
- do.SetData(cPickle.dumps(data,1))
+class FormWidget(DocumentWidget):
+ """
+ A DocumentWidget specific to the forms document type
+ """
- return do
+ # If Char:height or Char:width aren't specified, assume...
+ char_default_height = 1
+ char_default_width = 10
- def SelectedHitTest(self, x, y):
- if self.bounds.x <= x <= self.bounds.x + self.bounds.width and \
- self.bounds.y <= y <= self.bounds.y + self.bounds.height:
- return True
- else:
- return False
+ # Specific widgets can redefine these if they can't go smaller or larger
+ # than a certain size (e.g., labels are always 1 char high)
+ char_min_height = 1
+ char_min_width = 1
+ char_max_height = 99999999
+ char_max_width = 99999999
+
+ # Compensation for the border drawn around objects if selected
+ border_compensation = 4
+
+
+ def calc_metrics(self):
+
+ canvas = self.canvas
+
+ object = self.gobject
+ # This gives us an offset onto the current drawing canvas
+ # accounting for any workspace margins.
+ documentX = canvas.document_origin_x
+ documentY = canvas.document_origin_y
+ # Right now, only Char positioning is supported.
+ x = object['Char:x']
+ y = object['Char:y']
+
+ try:
+ w = object['Char:width']
+ except KeyError:
+ w = self.char_default_width
+
+ try:
+ h = object['Char:height']
+ except KeyError:
+ h = self.char_default_height
+
+ # Convert from Char positions into wx positions
+ x *= xscale
+ y *= yscale
+ h *= xscale
+ w *= xscale
+
+ # Used to figure in compensation needed for selection box
+ # decorations and borders
+ compensation = self.border_compensation
+
+ # Save the bounds, relative to the canvas, of the area
+ # that needs to be refreshed when this widget changes.
+ # (Includes all generic decoration, like selection borders)
+ self.refresh_bounds = wx.Rect(x + documentX - compensation,
+ y + documentY - compensation,
+ w + compensation * 2,
+ h + compensation * 2)
+
+ # Save the bounds, relative to the canvas, of the area
+ # that the widget sans decorations occupy on the
+ self.hit_test_bounds = wx.Rect(x + documentX,
+ y + documentY,
+ w, h)
+
+ # Save the bounds, relative to being drawn in a context at (0,0)
+ self.draw_bounds = wx.Rect(compensation, compensation, w, h)
+
+
+
+ #def move(self, x, y):
+
+
+class LabelWidget(FormWidget):
+
+ char_max_height = 1
+
+ def draw_widget(self, dc):
+ dc.SetPen(wx.Pen(colorIndex['text']))
+ object = self.gobject
+ x, y, w, h = self.draw_bounds.Get()
+ dc.SetFont(wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT))
+ # Center each character in its cell
+ for char in object.text:
+ cw, ch = dc.GetTextExtent(char)
+ dc.DrawText(char, x + int((xscale - cw)/2), y)
+ x += xscale
+
+
+class EntryWidget(FormWidget):
+ def draw_widget(self, dc):
+ dc.SetPen(wx.Pen(colorIndex['widget']))
+ x,y,w,h = self.draw_bounds.Get()
+ dc.DrawRoundedRectangle(x,y,w,h, 1)
+
+
+class UnknownWidget(FormWidget):
+ def draw_widget(self, dc):
+ dc.SetPen(wx.Pen(colorIndex['widget']))
+ object = self.gobject
+ x,y,w,h = self.draw_bounds.Get()
+ dc.SetFont(wx.SystemSettings_GetFont(wx.SYS_DEFAULT_GUI_FONT))
+ dc.DrawRoundedRectangle(x,y,w,h, 4)
+
+
#==========================================================================
# Canvas implementation
#==========================================================================
-class PaintCanvas(DocumentCanvas):
- def draw_document_frame(self, dc):
- object = self._object
+class PainterCanvas(DocumentCanvas):
+
+ def __init__(self, *args, **parms):
+ DocumentCanvas.__init__(self, *args, **parms)
+
+ self.document_origin_x = xscale * 2
+ self.document_origin_y = xscale * 2
+
+
+
Modified:
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
===================================================================
--- trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
2006-03-31 22:42:13 UTC (rev 8330)
+++ trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
2006-03-31 23:46:55 UTC (rev 8331)
@@ -20,10 +20,6 @@
#
# $Id: TriggerEditor.py 8210 2006-03-07 00:04:31Z jcater $
-#
-# These classes were based on SimpleCanvas from OSA's Chandler
-# application, but heavily commented, debugged, and extended.
-#
import time
@@ -31,6 +27,17 @@
from colors import colorIndex, buildColorIndex
+cursor_map = {'resize-top': wx.CURSOR_SIZENS,
+ 'resize-bottom': wx.CURSOR_SIZENS,
+ 'resize-left': wx.CURSOR_SIZEWE,
+ 'resize-right': wx.CURSOR_SIZEWE,
+ 'resize-ul': wx.CURSOR_SIZENWSE,
+ 'resize-lr': wx.CURSOR_SIZENWSE,
+ 'resize-ll': wx.CURSOR_SIZENESW,
+ 'resize-ur': wx.CURSOR_SIZENESW,
+ 'move': wx.CURSOR_SIZING,
+ }
+
# ===================================================================
# The canvas to draw on
# ===================================================================
@@ -46,20 +53,56 @@
if not colorIndex:
buildColorIndex()
+ self.ordered_widget_list = []
- self.autoCreateDistance = 0
- self.zOrderedDrawableObjects = []
- self.internalDnDItem = None
self.Bind(wx.EVT_PAINT, self.__wx_on_paint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.__wx_on_erase_background)
self.Bind(wx.EVT_MOUSE_EVENTS, self.__wx_on_mouse_event)
+
+ # Mouse events
+ self.__mouse_hot_spots = []
+ self.__current_cursor = None
+ self.__mouse_mode = 'select'
+ self.__mouse_click_function = None
+ self.__mouse_current_action = ""
+
def __del__(self):
- for item in self.zOrderedDrawableObjects:
+ for item in self.ordered_widget_list:
item.Destroy()
+ self.ordered_widget_list = None
- def set_grid_size(self, x, y):
+ def set_mouse_mode(self, mode='select', function=None):
+ """
+ mode String that is one of:
+
+ select: The default mouse behavior, in which existing objects
+ can be selected, resized, moved, etc.
+
+ edit: Clicking any object "edits" that object. (Mainly,
+ used to change text on labels). Clicking the canvas
+ has no action.
+
+ create: Clicking the canvas creates a new object. This mode
+ doesn't actually do anything... your "method" passed in
+ should handle the actual creation.
+
+ delete: Clicking an object "deletes" the object. This mode
+ doesn't do anything ... your method should handle it.
+
+
+ method A python method to call once the target action happens.
+
+ """
+ self.__mouse_mode = mode
+ self.__mouse_click_function = method
+
+
+ def set_grid_scale(self, x, y):
+ """
+ Sets the grid x,y spacing
+ """
self.grid_x_spacing = x
self.grid_y_spacing = y
@@ -70,20 +113,19 @@
def __wx_on_paint(self, event):
"""
- wx.BufferedDC doesn't work here since it doesn't handle scrolled
windows
- and always allocates a buffer the size of the client area. So instead
we'll
- allocate a wx.MemoryDC, draw into it then blit it to our paintDC.
- Eventually, if we're printing, we should bypass the wx.MemoryDC.
- The updateRegion is not in scrolled coordinates.
+ Catch the wx OnPaint event and draw the canvas and any widgets
"""
+
+ # The updateRegion is not in scrolled coordinates.
scrollWindowOriginX, scrollWindowOriginY =
self.CalcUnscrolledPosition(0, 0)
+ # wx.BufferedDC doesn't work here since it doesn't handle scrolled
windows
+ # and always allocates a buffer the size of the client area. So
instead we'll
+ # allocate a wx.MemoryDC, draw into it then blit it to our paintDC.
paintDC = wx.PaintDC(self)
self.PrepareDC(paintDC)
- """
- Calculate the rectangle that needs updating in scrolled coordinates
- """
+ # Calculate the rectangle that needs updating in scrolled coordinates
updateRect = self.GetUpdateRegion().GetBox()
bufferX = updateRect.GetLeft() + scrollWindowOriginX
bufferY = updateRect.GetTop() + scrollWindowOriginY
@@ -95,9 +137,7 @@
memoryDC.SelectObject(offscreenBuffer)
memoryDC.SetDeviceOrigin(-bufferX, -bufferY)
- """
- Debugging code that makes it easy to see which areas are updating.
- """
+ ## Debugging code that makes it easy to see which areas are updating.
if 0:
success = paintDC.Blit(bufferX, bufferY,
bufferWidth, bufferHeight,
@@ -116,8 +156,8 @@
self.draw_background(memoryDC)
self.draw_widgets(memoryDC)
- self.draw_selection(memoryDC)
+ # Move the offscreen bitmap onto the scrolled window
paintDC.Blit(bufferX, bufferY,
bufferWidth, bufferHeight,
memoryDC,
@@ -126,29 +166,110 @@
memoryDC.EndDrawing()
def draw_widgets(self, dc):
+ """
+ Draw the individual widgets
+ """
+
updateRegion = self.GetUpdateRegion()
- scrollWindowOriginX, scrollWindowOriginY =
self.CalcUnscrolledPosition(0, 0)
+
+ (scrollWindowOriginX,
+ scrollWindowOriginY) = self.CalcUnscrolledPosition(0, 0)
+
dcOriginX, dcOriginY = dc.GetDeviceOrigin()
- index = len(self.zOrderedDrawableObjects) - 1
+
+ selection_bounds = None
+
+ index = len(self.ordered_widget_list) - 1
+
+
while index >= 0:
- drawableObject = self.zOrderedDrawableObjects [index]
- objectX = drawableObject.bounds.GetLeft()
- objectY = drawableObject.bounds.GetTop()
- objectWidth = drawableObject.bounds.GetWidth()
- objectHeight = drawableObject.bounds.GetHeight()
+ drawableObject = self.ordered_widget_list [index]
+ refresh_bounds = drawableObject.refresh_bounds
+
+ (objectX, objectY,
+ objectWidth, objectHeight) = refresh_bounds.Get()
+ # Calculate the bounds, offset by the scrollwindow's origin
bounds = wx.Rect(objectX - scrollWindowOriginX,
- objectY - scrollWindowOriginY,
- objectWidth,
- objectHeight)
+ objectY - scrollWindowOriginY,
+ objectWidth, objectHeight)
- if updateRegion.ContainsRect(bounds) != wx.OutRegion and
drawableObject.visible:
- dc.SetDeviceOrigin(objectX + dcOriginX, objectY + dcOriginY)
- drawableObject.draw(dc)
- dc.SetDeviceOrigin(dcOriginX, dcOriginY)
+
+
+ # Keep track of the overall bounds of all the selected
+ # widgets, so we can draw a selection box later
+ if drawableObject.selected:
+ if selection_bounds is None:
+ selection_bounds = bounds
+ else:
+ selection_bounds = selection_bounds.Union(bounds)
+
+ if (updateRegion.ContainsRect(bounds) != wx.OutRegion and
+ drawableObject.visible):
+
+ dc.SetDeviceOrigin(objectX + dcOriginX, objectY + dcOriginY)
+ drawableObject.draw(dc)
+ dc.SetDeviceOrigin(dcOriginX, dcOriginY)
+
index -= 1
+
+ # TODO: Somehow move most of this logic out of draw...
+
+ # Draw a box around all the selected widgets
+ if selection_bounds is not None:
+ self.__mouse_hot_spots = []
+ add_hotspot = self.__mouse_hot_spots.append
+
+ x, y, w, h = selection_bounds.Get()
+
+ # Not sure why we have to do this, but it makes the
+ # box draw correctly.
+ x += 2
+ y += 2
+ w -= 4
+ h -= 4
+
+ # TODO: quick fix for enforcing a min size (fix!)
+ w = max(w, 17)
+ h = max(h, 17)
+ selection_bounds = self.__selection_bounds = wx.Rect(x,y,w,h)
+
+ # Draw the main border
+ dc.SetBrush(wx.Brush(wx.BLACK, wx.TRANSPARENT))
+ dc.SetPen(wx.Pen(colorIndex['selectionframe']))
+ dc.DrawRectangleRect(selection_bounds)
+
+ # Draw the sizer grippers
+ dc.SetBrush(wx.Brush(wx.WHITE))
+ for bx, by, position in (
+ (x - 2, y - 2, 'resize-ul'), # upper left
+ (x + w - 3, y - 2, 'resize-ur'), # upper right
+ (x - 2, y + h - 3, 'resize-ll'), # lower left
+ (x + w - 3, y + h - 3, 'resize-lr'), # lower right
+ (x - 2 + w / 2, y - 2, 'resize-top'), # top middle
+ (x - 2, y - 2 + h / 2,'resize-left'), # left middle
+ (x + w - 3, y - 2 + h / 2, 'resize-right'), # right middle
+ (x - 2 + w / 2 , y + h - 3, 'resize-bottom'), # bottom middle
+ ):
+
+ rect = wx.Rect(bx,by,5,5)
+ dc.DrawRectangleRect(rect)
+ add_hotspot((rect,position))
+
+ # Add hotspot markers for the selection box
+ add_hotspot((wx.Rect(x, y, 1, h), 'resize-left'))
+ add_hotspot((wx.Rect(x, y, w, 1), 'resize-top'))
+ add_hotspot((wx.Rect(x + w - 1, y, 1, h), 'resize-right'))
+ add_hotspot((wx.Rect(x, y + h - 1, w, 1), 'resize-bottom'))
+ add_hotspot((wx.Rect(x + 1, y + 1, w - 1, h - 1), 'move'))
+
+
+
def draw_background(self, dc):
+ """
+ Draw a background (with grid if appropriate)
+ """
dc.SetBackgroundMode(wx.TRANSPARENT)
dc.Clear()
#
@@ -170,6 +291,7 @@
dc.DrawLine(x,0,x,h-1)
if switchToMainPen:
dc.SetPen(pen1)
+ switchToMainPen = False
# Draw horizontal grid lines
@@ -181,6 +303,7 @@
dc.DrawLine(0, y, w, y)
if switchToMainPen:
dc.SetPen(pen1)
+ switchToMainPen = False
elif self.gridStyle == 2:
SOLIDSPACING = 4
@@ -205,174 +328,279 @@
dc.DrawLine(0, y, w, y)
- def draw_selection(self, dc):
- print "Draw da box, fewl"
-
def __wx_on_erase_background(self, event):
"""
- Override __wx_on_erase_background to avoid erasing background. Instead
+ Override __wx_on_erase_background to avoid erasing background. Instead
implement OnDrawBackground to draw/erase the background. This
design alternative will eliminate flicker
"""
pass
+
+ # ---------------------------------------------------------------
+ # Mouse events
+ # ---------------------------------------------------------------
def __wx_on_mouse_event(self, event):
x, y = event.GetPositionTuple()
x, y = self.CalcUnscrolledPosition(x, y)
- for drawableObject in self.zOrderedDrawableObjects:
- if drawableObject.bounds.Inside((x, y)):
- event.m_x = x - drawableObject.bounds.GetX()
- event.m_y = y - drawableObject.bounds.GetY()
- if drawableObject.ProcessEvent(event):
- return True
- if self.autoCreateDistance != 0:
- if event.ButtonDown() and self.CreateHitTest(x, y):
- self.dragStart = wx.Point(x, y)
- self.CaptureMouse()
- return True
- elif hasattr(self, 'dragStart'):
- if event.Dragging():
- """
- Clip mouse position to the scrolling window's bounds
- """
- boundsX, boundsY = self.GetVirtualSizeTuple()
- if x < 0:
- x = 0
- if x > boundsX:
- x = boundsX
- if y < 0:
- y = 0
- if y > boundsY:
- y = boundsY
+ mode = self.__mouse_mode
+ current_action = self.__mouse_current_action
+
+ if event.Dragging():
+ if mode == 'select':
+ if current_action == 'rubberband':
+ self.__update_rubberband(x,y)
+
+ elif current_action == 'move':
+ self.__update_moving(x,y)
+
+ elif current_action == 'resize':
+ self.__update_resizing(x,y)
+
+
+ elif event.Moving():
+ cursor = None
+ if mode == 'select':
+ if not current_action:
+ # No previous action. Should we change the cursor?
+ for rect, position in self.__mouse_hot_spots:
+ if rect.InsideXY(x, y):
+ cursor = cursor_map[position]
+ break
+ if not cursor:
+ cursor = wx.CURSOR_ARROW
- deltaX = x - self.dragStart.x
- deltaY = y - self.dragStart.y
+ if cursor != self.__current_cursor:
+ self.SetCursor(wx.StockCursor(cursor))
+
+ return True
- if deltaX >= 0:
- left = self.dragStart.x
- width = deltaX
- else:
- left = x
- width = -deltaX
+
+ elif event.LeftDown():
- if deltaY >= 0:
- top = self.dragStart.y
- height = deltaY
- else:
- top = y
- height = -deltaY
- dragRect = wx.Rect(left, top, width, height)
+ if mode == 'select':
- if not hasattr(self, 'dragCreateDrawableObject'):
- if(deltaX * deltaX) +(deltaY * deltaY) >(self.autoCreateDistance
* self.autoCreateDistance):
- """
- Create a new drawable object if we've dragged
autoCreateDistance
- pixels
- """
- object = self.dragCreateDrawableObject =
self.CreateNewDrawableObject(dragRect,
-
self.dragStart,
-
wx.Point(x, y))
- # if we weren't allowed to create one, give up
- if object is None:
- return True
+ if not current_action:
+ # No previous action.
+ new_action = None
+
+ # Check to see if a currently selected area has been
+ # clicked, and whether it will be moved or resized:
+ for rect, action in self.__mouse_hot_spots:
+ if rect.InsideXY(x, y):
+ new_action = action
+ break
- self.DeSelectAll()
- object.selected = True
+ # Check to see if any widgets were clicked
+ if not new_action:
+ for widget in self.ordered_widget_list:
+ if widget.select_hit_test(x,y):
+ new_action = 'move'
+ break
+
+ # Finally, assume we're creating a rubberband selector
+ if not new_action:
+ new_action = 'rubberband'
+ self.__start_rubberband(x, y)
+
+ elif new_action == 'move':
+ self.__start_moving(x,y)
+ elif new_action.startswith('resize-'):
+ self.__start_resizing(x, y,
+ new_action.split('-')[1])
+ new_action = 'resize'
- #self.zOrderedDrawableObjects.insert(0,
self.dragCreateDrawableObject)
-
#self.refresh_scrolled_rect(self.dragCreateDrawableObject.bounds);
- self.add(object)
+ self.__mouse_current_action = new_action
+
+ return True
+
+
+ elif event.LeftUp():
+
+ if mode == 'select':
+
+ if current_action == 'rubberband':
+ self.__stop_rubberband()
+
+ elif current_action == 'move':
+ self.__stop_moving()
+
+ elif current_action == 'resize':
+ self.__stop_resizing()
+
+ self.__mouse_current_action = ''
+
return True
- else:
- if self.dragCreateDrawableObject != None:
- self.dragCreateDrawableObject.SizeDrag(dragRect,
- self.dragStart,
- wx.Point(x, y))
- elif event.ButtonUp():
- del self.dragStart
- if hasattr(self, 'dragCreateDrawableObject'):
- del self.dragCreateDrawableObject
- self.ReleaseMouse()
- return True
+
return False
+
+
+
+ # ---------------------------------------------------------------
+ # Get all selected widgets
+ # ---------------------------------------------------------------
+ def get_selected_widgets(self):
+ """
+ Return a list of the currently selected widgets
+ """
+ results = []
+ append = results.append
+ for widget in self.ordered_widget_list:
+ if widget.selected:
+ append(widget)
+
+ return results
+
+
+ # ---------------------------------------------------------------
+ # Support for visual moving hints
+ # ---------------------------------------------------------------
+ def __start_moving(self, x, y):
+ self.__mouse_move_items = widgets = self.get_selected_widgets()
+ self.CaptureMouse()
+ for widget in widgets:
+ widget.selected = False
+ widget.show(False)
+
+ def __update_moving(self, x, y):
+ pass
+
+ def __stop_moving(self):
+ self.ReleaseMouse()
+ for widget in self.__mouse_move_items:
+ widget.selected = True
+ widget.show(True)
+ self.__mouse_move_items = None
+
+ # ---------------------------------------------------------------
+ # Support for visual resizing hints
+ # ---------------------------------------------------------------
+ def __start_resizing(self, x, y, orientation):
+ self.__mouse_resize_orientation = orientation
+ self.CaptureMouse()
+
+ def __update_resizing(self, x, y):
+ orientation = self.__mouse_resize_orientation
+
+ def __stop_resizing(self):
+ self.ReleaseMouse()
+
+
+ # ---------------------------------------------------------------
+ # Support for click and drag rubberband boxes
+ # ---------------------------------------------------------------
+ def __start_rubberband(self, x, y):
+ """
+ Start a rubberband box
+ """
+ self.__mouse_rubberband_orig_cursor = self.GetCursor()
+ self.__mouse_rubberband_start = (x, y)
+ self.__mouse_rubberband_last = None
+ self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
+ self.CaptureMouse()
+
+ def __update_rubberband(self, x=None, y=None):
+ """
+ Draw one box shape and possibly erase another.
+ """
+ dc = wx.ClientDC(self)
+ dc.BeginDrawing()
+ dc.SetPen(wx.Pen(wx.WHITE, 1, wx.DOT))
+ dc.SetBrush(wx.TRANSPARENT_BRUSH)
+ dc.SetLogicalFunction(wx.XOR)
+
+ start = self.__mouse_rubberband_start
+ last = self.__mouse_rubberband_last
+
+ # Erase the old box
+ if last:
+ r = wx.RectPP(start, last)
+ dc.DrawRectangleRect(r)
+
+ # Draw the new box
+ if x is not None:
+ r = wx.RectPP(start, (x,y))
+ dc.DrawRectangleRect(r)
+ self.__mouse_rubberband_last = (x,y)
+
+ dc.EndDrawing()
+
+
+ def __stop_rubberband(self):
+ """
+ Reset the cursor
+ """
+ self.SetCursor(self.__mouse_rubberband_orig_cursor)
+ self.__update_rubberband(None)
+ self.ReleaseMouse()
+
+
+ # ---------------------------------------------------------------
+ # Drag and Drop support
+ # ---------------------------------------------------------------
def OnData(self, dataObject, x, y, result):
"""
Handle default behavior of copy and move
"""
if result == wx.DragMove or result == wx.DragCopy:
if(self.internalDnDItem != None) and(result == wx.DragMove):
- assert(self.zOrderedDrawableObjects.count(self.internalDnDItem)
== 1)
+ assert(self.ordered_widget_list.count(self.internalDnDItem) == 1)
drawableObject =
self.ConvertDataObjectToDrawableObject(dataObject, x, y, True)
- x = drawableObject.bounds.GetLeft()
- y = drawableObject.bounds.GetTop()
+ x = drawableObject.refresh_bounds.GetLeft()
+ y = drawableObject.refresh_bounds.GetTop()
- self.zOrderedDrawableObjects.remove(self.internalDnDItem)
- self.zOrderedDrawableObjects.insert(0, self.internalDnDItem)
+ self.ordered_widget_list.remove(self.internalDnDItem)
+ self.ordered_widget_list.insert(0, self.internalDnDItem)
self.internalDnDItem.MoveTo(x, y)
self.internalDnDItem.Show(True)
else:
drawableObject =
self.ConvertDataObjectToDrawableObject(dataObject, x, y, False)
- x = drawableObject.bounds.GetLeft()
- y = drawableObject.bounds.GetTop()
+ x = drawableObject.refresh_bounds.GetLeft()
+ y = drawableObject.refresh_bounds.GetTop()
- self.zOrderedDrawableObjects.insert(0, drawableObject)
- self.refresh_scrolled_rect(drawableObject.bounds);
+ self.ordered_widget_list.insert(0, drawableObject)
+ drawableObject.force_canvas_refresh()
return result
- def CreateHitTest(self, x, y):
- """
- Set self.autoCreateDistance to some value other than zero to enable
- dragging autoCreateDistance pixels to automatically create new drawable
- objects.
- By default, drawable objects are created if you click and drag in the
- canvas anwhere there isn't a drawable object. You can restrict this
location
- that drawable objects are created by overriding this routine
- """
- return True
- """
- You must implement the following functions
- """
- def ConvertDataObjectToDrawableObject(self, dataObject, x, y, move):
- """
- You must implement this routine to convert a dataobject, used in
- drag and drop into a drawable object.
- """
- assert(False)
- def CreateNewDrawableObject(self, dragRect, startDrag, endDrag):
- """
- You must implement this routine to create new drawable objects by
- dragging on the blank canvas.
- """
- assert(False)
+ # ---------------------------------------------------------------
+ #
+ # ---------------------------------------------------------------
- def DeSelectAll(self):
+ def deselect_all(self):
"""
Mark all selected objects as not selected. Only Refresh the objects
that change, don't call Update.
"""
- for drawableObject in self.zOrderedDrawableObjects:
- if(drawableObject.selected):
+ for drawableObject in self.get_selected_widgets():
+ if (drawableObject.selected):
drawableObject.selected = False
- self.refresh_scrolled_rect(drawableObject.bounds)
+ drawableObject.force_canvas_refresh()
- def add(self, object):
+ def add(self, widget):
"""
- Add a DrawableObject to the canvas
+ Add a DocumentWidget to the canvas
"""
- self.zOrderedDrawableObjects.insert(0, object)
- self.refresh_scrolled_rect(object.bounds);
+ self.ordered_widget_list.insert(0, widget)
+ widget.force_canvas_refresh()
- def remove(self, object):
- self.zOrderedDrawableObjects.remove(object)
- bounds = object.bounds
- self.refresh_scrolled_rect(bounds)
- object.Destroy()
+
+ def remove(self, widget):
+ """
+ Remove a DocumentWidget from the canvas
+ """
+ self.ordered_widget_list.remove(widget)
+ bounds = widget.refresh_bounds
+ self.refresh_scrolled_rect(refresh_bounds)
+ widget.Destroy()
+
+
+
Modified:
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/colors.py
===================================================================
--- trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/colors.py
2006-03-31 22:42:13 UTC (rev 8330)
+++ trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/colors.py
2006-03-31 23:46:55 UTC (rev 8331)
@@ -29,8 +29,8 @@
colorIndex = {}
def buildColorIndex():
- colorIndex['selectionframe'] =
wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
- colorIndex['selectedframe'] =
wx.SystemSettings_GetColour(wx.SYS_COLOUR_HIGHLIGHT)
+ colorIndex['selectionframe'] = wx.BLUE #
wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT)
+ colorIndex['selectedframe'] = wx.BLUE #
wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNHIGHLIGHT)
colorIndex['workspace'] =
wx.SystemSettings_GetColour(wx.SYS_COLOUR_APPWORKSPACE)
colorIndex['workspaceGrid'] = wx.Colour(240,240,240) # TODO: ???
colorIndex['panel'] = wx.SystemSettings_GetColour(wx.SYS_COLOUR_BACKGROUND)
Modified:
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
===================================================================
--- trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
2006-03-31 22:42:13 UTC (rev 8330)
+++ trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
2006-03-31 23:46:55 UTC (rev 8331)
@@ -20,10 +20,7 @@
#
# $Id: TriggerEditor.py 8210 2006-03-07 00:04:31Z jcater $
-#
-# These classes were based on SimpleCanvas from OSA's Chandler
-# application, but heavily commented, debugged, and extended.
-#
+import cPickle
import wx
@@ -34,33 +31,44 @@
# A Drawable canvas widget
# ===================================================================
class DocumentWidget(wx.EvtHandler):
- def __init__(self, canvas):
+ def __init__(self, gobject, canvas):
super(DocumentWidget, self).__init__()
- self.bounds = wx.Rect()
+ self.gobject = gobject
+
self.canvas = canvas
self.visible = True
self.selected = False
- self.dragStartPos = None
- self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)
+ # Coordinates (relative to canvas) that should register hit tests
+ self.hit_test_bounds = wx.Rect()
+ self.hit_test_exclusions = [] # Can be a list of wx.Rect()
- def MoveTo(self, x, y):
- self.canvas.refresh_scrolled_rect(self.bounds);
- self.bounds.SetLeft(x)
- self.bounds.SetTop(y)
- self.canvas.refresh_scrolled_rect(self.bounds);
+ # Coordinates (relative to canvas) to refresh on widget updates
+ self.refresh_bounds = wx.Rect()
- def SetSize(self, width, height):
- self.canvas.refresh_scrolled_rect(self.bounds);
- self.bounds.SetWidth(width)
- self.bounds.SetHeight(height)
- self.canvas.refresh_scrolled_rect(self.bounds);
+ # Calculate defaults for the above
+ self.recalc_metrics()
- def OnMouseEvent(self, event):
+ self.Bind(wx.EVT_MOUSE_EVENTS, self.__wx_mouse_event)
+
+
+ def drag_move(self, x, y):
+ self.force_canvas_refresh()
+ self.recalc_metrics()
+ self.force_canvas_refresh()
+
+
+ def drag_resize(self, width, height):
+ self.force_canvas_refresh()
+ # TODO
+ self.force_canvas_refresh()
+
+
+ def __wx_mouse_event(self, event):
x, y = event.GetPositionTuple()
if event.ButtonDown(1):
- if self.SelectedHitTest(x, y) :
+ if self.select_hit_test(x, y) :
self.canvas.DeSelectAll()
self.SetSelected()
self.canvas.Update()
@@ -79,23 +87,18 @@
event.Skip()
return False
- def SetBounds(self, bounds):
- self.canvas.refresh_scrolled_rect(self.bounds);
- self.bounds = bounds
- self.canvas.refresh_scrolled_rect(self.bounds);
-
- def Show(self, show):
+ def show(self, show):
# Make sure show is a Boolean
show = bool(show)
if(show ^ self.visible):
self.visible = show
- self.canvas.refresh_scrolled_rect(self.bounds);
+ self.force_canvas_refresh()
self.canvas.Update()
def ConvertToCanvasDeviceCoordinates(self, x, y):
- return self.canvas.CalcScrolledPosition(self.bounds.GetLeft() + x,
- self.bounds.GetTop() + y)
+ return self.canvas.CalcScrolledPosition(self.refresh_bounds.GetLeft() +
x,
+ self.refresh_bounds.GetTop() +
y)
def DoDrag(self, x, y):
"""
@@ -105,7 +108,7 @@
"""
Create the dragImage and begin dragging over the full screen
"""
- offscreenBuffer = wx.EmptyBitmap(self.bounds.GetWidth(),
self.bounds.GetHeight())
+ offscreenBuffer = wx.EmptyBitmap(self.refresh_bounds.GetWidth(),
self.refresh_bounds.GetHeight())
memoryDC = wx.MemoryDC()
memoryDC.SelectObject(offscreenBuffer)
@@ -113,7 +116,7 @@
memoryDC.Clear()
self.draw(memoryDC)
- maskBitmap = wx.EmptyBitmap(self.bounds.GetWidth(),
self.bounds.GetHeight(), 1)
+ maskBitmap = wx.EmptyBitmap(self.refresh_bounds.GetWidth(),
self.refresh_bounds.GetHeight(), 1)
memoryDC.SelectObject(maskBitmap)
memoryDC.SetBackground(wx.BLACK_BRUSH)
@@ -143,7 +146,7 @@
self.dragImage.Show()
"""
- We need to keep a reference to the dataObject, rather than create
+ We need to keep a reference to the dataObject, rather than create
it in the construction because wxCanvasDropSource doesn't own the
data so the garbage collector will delete it.
"""
@@ -157,56 +160,141 @@
self.dragImage.EndDrag()
del self.dragImage
- def draw_mask(self, dc):
+ def SizeDrag(self, dragRect, startDrag, endDrag):
+ self.force_canvas_refresh()
+ #self.bounds = dragRect
+ self.force_canvas_refresh()
+
+ def set_selected(self, selected=True):
+ """
+ Sets the selected bit for this object. Does a canvas refresh on
+ the object if it has changed state. Does not call Update on the canvas.
+ """
+ selected = bool(selected)
+
+ # Only do a refresh if we've changed state.
+ if (selected ^ self.selected):
+ self.selected = selected
+ self.force_canvas_refresh()
+
+
+ def recalc_metrics(self):
"""
- optionally implement this routine to draw a mask
+ Force a recalculation of all metric information
"""
- pass
+ self.calc_metrics()
+
- def SizeDrag(self, dragRect, startDrag, endDrag):
- self.canvas.refresh_scrolled_rect(self.bounds)
- self.bounds = dragRect
- self.canvas.refresh_scrolled_rect(self.bounds)
+ def force_canvas_refresh(self):
+ """
+ Signal to the canvas that are area changed
+ """
+ self.canvas.refresh_scrolled_rect(self.refresh_bounds)
+
- """
- You must implement the following functions
- """
def draw(self, dc):
- """
- You must implement this routine to do draw
- """
- assert(False)
+ """
+ Draw the widget and any decorations.
+ """
+ self.draw_widget(dc)
+ if self.selected:
+ self.draw_border(dc)
- def DragHitTest(self, x, y):
- """
+
+ # ====================================================================
+ # You can implement the following functions, but usually won't need to
+ # ====================================================================
+
+ def draw_border(self, dc):
+ """
+ Draw a border for selected widgets
+ """
+ x, y, w, h = self.hit_test_bounds.Get()
+ dc.SetBrush(wx.Brush(wx.BLACK, wx.TRANSPARENT))
+ dc.SetPen(wx.Pen(colorIndex['selectedframe']))
+ dc.DrawRectangle(3, 3, w + 2, h + 2)
+
+
+ def select_hit_test(self, x, y):
+ """
+ You must implement this routine to do hit testing to identify
+ the region for selecting the object.
+
+ By default, if not subclassed, we just use the bounds defined for the
+ object.
+ """
+ self.hit_test_bounds.InsideXY(x,y)
+
+
+ def drag_hit_test (self, x1, y1, x2, y2):
+ """
You must implement this routine to do hit testing for dragable region
- of drawable object
- """
- assert(False)
+ of drawable object
+
+ By default, we use the bounds of the object.
+ """
+ # Should also take into account self.hit_test_exclusions
+ dragRect = wx.Rect((x1, y1), (x2, y2))
+ result = self.hit_test_bounds.Intersects(dragRect)
+
+ if result:
+ # If the entire dragRect is in an exclusion area, return false
+ for exclusion in self.hit_test_exclusions:
+ if exclusion.Intersect(dragRect) == dragRect:
+ return False
+
+ return result
- def ConvertDrawableObjectToDataObject(self, x, y):
- """
+
+ def add_to_clipboard (self, x, y):
+ """
You must implement this routine to create data object for drag and drop
- """
- assert(False)
+ """
+ object = self
- def SelectedHitTest(self, x, y):
+ # TODO: This is a placeholder only! Not implemented yet.
+ data = [ { "Type" : "selectedWidgets",
+ "Attributes": {
+ 'startingX': x,
+ 'startingY': y,
+ }
+ }]
+ # "name": dname,
+ # "table": object.name,
+ # "database": self.connectionName } }
+ #
+ #
+ do =
wx.CustomDataObject(wx.CustomDataFormat("application/x-gnue-designer"))
+ do.SetData(cPickle.dumps(data,1))
+
+ return do
+
+
+ def draw_mask(self, dc):
"""
- You must implement this routine to do hit testing to identify the region
- for selecting the object.
+ optionally implement this routine to draw a mask
+ (Not sure what this means... inherited from SimpleCanvas)
"""
- assert(False)
+ pass
- def SetSelected(self, selected=True):
+
+
+ # ====================================================================
+ # You *must* implement the following functions
+ # ====================================================================
+
+ def calc_metrics(self):
+ """
+ Calculate hit_test_bounds, refresh_bounds, and any
+ class-specific bounds your draw_widget methods might need.
+ """
+ assert(False)
+
+
+ def draw_widget(self, dc):
"""
- Sets the selected bit for this object. Does a canvas refresh on the
object
- if it has changed state. Does not call Update on the canvas.
+ You must implement this routine to do draw the actual widget
"""
- # Use the same trick as Show()
- selected = bool(selected)
+ assert(False)
- # Only do a refresh if we've changed state.
- if(selected ^ self.selected):
- self.selected = selected
- self.canvas.refresh_scrolled_rect(self.bounds);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r8331 - in trunk/gnue-designer/src: forms/PagePainter uidrivers/wx/uihelpers/documentcanvas,
jcater <=