1 # Copyright (c) 2009 Paolo Capriotti <p.capriotti@gmail.com>
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 require 'observer_utils'
10 class Scene < Qt::GraphicsScene
11 MINIMAL_DRAG_DISTANCE = 3
16 self.background_brush = $qApp.palette.brush(Qt::Palette::Window)
20 def add_clickable_element(element)
24 def mousePressEvent(e)
25 if e.button == Qt::LeftButton
26 pos = e.scene_pos.to_i
28 @drag_data = { :pos => pos }
33 def mouseReleaseEvent(e)
34 if e.button == Qt::LeftButton
36 old_pos = @drag_data[:pos]
37 item = @drag_data[:item]
41 pos = e.scene_pos.to_i
42 element_src = find_element(old_pos)
43 element_dst = find_element(pos)
46 # normal drag and drop
49 # if the drop is in a blank area,
50 # notify the source of the drop
51 notify(element_src, :drop, [old_pos, nil], data)
53 src = if element_src == element_dst
56 # if the drag and drop is close and there's no
57 # dragged item, notify a click instead
59 same_square(element_dst, src, pos) and
61 notify(element_dst, :click, [pos])
63 notify(element_dst, :drop, [src, pos], data)
66 elsif element_src == element_dst
67 # close drag and drop == click, unless
68 # old_pos and pos fall on different squares
69 if same_square(element_src, old_pos, pos)
70 notify(element_dst, :click, [pos])
73 # a rapid drag and drop between different elements
74 # is never considered a click
75 notify(element_src, :drag, [old_pos])
76 notify(element_src, :drop, [nil, pos], data)
84 pos = e.scene_pos.to_i
85 if !@drag_data[:dragging]
86 dx = (@drag_data[:pos].x - pos.x).abs
87 dy = (@drag_data[:pos].y - pos.y).abs
88 if dx >= MINIMAL_DRAG_DISTANCE ||
89 dy >= MINIMAL_DRAG_DISTANCE
90 @drag_data[:dragging] = true
91 notify(find_element(pos), :drag, [@drag_data[:pos]])
98 @drag_data[:item].pos = (pos - @drag_data[:size] / 2).to_f
103 def find_element(pos)
104 @elements.detect do |element|
105 element.map_rect_to_scene(
106 element.map_rect_from_parent(
107 element.rect.to_f)).contains(pos.to_f)
111 def notify(element, event, pos, *args)
113 relpos = pos.map{|p| rel(element, p) }
114 element.send("on_#{event}", *(relpos + args))
118 def rel(element, pos)
120 element.map_from_scene(pos.to_f)
124 def same_square(element, pos1, pos2)
125 element.to_logical(rel(element, pos1)) ==
126 element.to_logical(rel(element, pos2))
129 # invoked by the controller when one of the elements
133 @drag_data = @drag_data.merge(data)
137 def remove_element(item)
138 @elements.delete(item)