selector.lua 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. local Selector = class()
  2. Selector.doubleClickSpeed = .25
  3. local function invoke(x, k, ...) return x.editor[k](x, ...) end
  4. function Selector:init()
  5. self.selection = {}
  6. self.active = false
  7. self.lastClick = tick
  8. self.lastButton = nil
  9. self.dragging = false
  10. self.dragStartX = nil
  11. self.dragStartY = nil
  12. self.depth = -100000
  13. ctx.view:register(self, 'draw')
  14. ctx.view:register(self, 'gui')
  15. end
  16. function Selector:update()
  17. self.active = love.keyboard.isDown('lshift')
  18. if self.active and love.mouse.isDown('l', 'r') and math.distance(self.dragStartX, self.dragStartY, love.mouse.getPosition()) > 5 then
  19. self.dragging = true
  20. end
  21. end
  22. function Selector:draw()
  23. love.graphics.setColor(0, 255, 255, 100)
  24. for _, prop in ipairs(self.selection) do
  25. if prop.shape then prop.shape:draw('fill') end
  26. end
  27. love.graphics.setColor(0, 255, 255)
  28. if self.active then
  29. table.each(ctx.map.props, function(prop)
  30. if prop.shape then prop.shape:draw('line') end
  31. end)
  32. end
  33. end
  34. function Selector:gui()
  35. if self.active and self.dragging then
  36. if love.keyboard.isDown('lctrl') then
  37. love.graphics.setColor(0, 255, 255, 150)
  38. love.graphics.line(self.dragStartX, self.dragStartY, love.mouse.getPosition())
  39. else
  40. love.graphics.setColor(0, 255, 255, 25)
  41. local x, y = self.dragStartX + .5, self.dragStartY + .5
  42. local w, h = love.mouse.getX() - self.dragStartX - 1, love.mouse.getY() - self.dragStartY - 1
  43. love.graphics.rectangle('fill', x, y, w, h)
  44. love.graphics.setColor(0, 255, 255, 150)
  45. love.graphics.rectangle('line', x, y, w, h)
  46. end
  47. end
  48. end
  49. function Selector:pointTest(x, y)
  50. local shapes = ctx.collision.hc:shapesAt(ctx.view:worldPoint(x, y))
  51. return table.map(shapes, function(s) return s.owner end)
  52. end
  53. function Selector:rectTest(x1, y1, x2, y2)
  54. if x1 > x2 then x1, x2 = x2, x1 end
  55. if y1 > y2 then y1, y2 = y2, y1 end
  56. x1, y1 = ctx.view:worldPoint(x1, y1)
  57. x2, y2 = ctx.view:worldPoint(x2, y2)
  58. local selectRect = ctx.collision.hc:addRectangle(x1, y1, x2 - x1, y2 - y1)
  59. local res = {}
  60. for shape in pairs(selectRect:neighbors()) do
  61. if selectRect:collidesWith(shape) then
  62. table.insert(res, shape.owner)
  63. end
  64. end
  65. ctx.collision.hc:remove(selectRect)
  66. return res
  67. end
  68. function Selector:lineTest(x1, y1, x2, y2)
  69. x1, y1 = ctx.view:worldPoint(x1, y1)
  70. x2, y2 = ctx.view:worldPoint(x2, y2)
  71. local dis = math.distance(x1, y1, x2, y2)
  72. local res = {}
  73. for shape in pairs(ctx.collision.hc:shapesInRange(math.min(x1, x2), math.min(y1, y2), math.max(x1, x2), math.max(y1, y2))) do
  74. local intersects, d = shape:intersectsRay(x1, y1, x2 - x1, y2 - y1)
  75. if intersects then
  76. table.insert(res, shape.owner)
  77. end
  78. end
  79. return res
  80. end
  81. function Selector:mousepressed(x, y, button)
  82. local function doubleClick()
  83. return button == self.lastButton and (tick - self.lastClick) * tickRate <= self.doubleClickSpeed
  84. end
  85. if self.active then
  86. if button == 'l' then
  87. if doubleClick() then
  88. self:selectAll()
  89. else
  90. self:select(unpack(self:pointTest(x, y)))
  91. end
  92. self.lastClick = tick
  93. self.lastButton = button
  94. self.dragStartX = x
  95. self.dragStartY = y
  96. elseif button == 'r' then
  97. if doubleClick() then
  98. self:deselectAll()
  99. else
  100. self:deselect(unpack(self:pointTest(x, y)))
  101. end
  102. self.lastClick = tick
  103. self.lastButton = button
  104. self.dragStartX = x
  105. self.dragStartY = y
  106. end
  107. end
  108. end
  109. function Selector:mousereleased(x, y, button)
  110. if self.active and self.dragging then
  111. local selector = love.keyboard.isDown('lctrl') and self.lineTest or self.rectTest
  112. local targets = selector(self, self.dragStartX, self.dragStartY, x, y)
  113. if button == 'l' then
  114. self:select(unpack(targets))
  115. elseif button == 'r' then
  116. self:deselect(unpack(targets))
  117. end
  118. self.dragging = false
  119. end
  120. end
  121. function Selector:select(prop, ...)
  122. if not prop then return end
  123. if not self.selection[prop] then
  124. table.insert(self.selection, prop)
  125. self.selection[prop] = #self.selection
  126. end
  127. return self:select(...)
  128. end
  129. function Selector:deselect(prop, ...)
  130. if not prop then return end
  131. if self.selection[prop] then
  132. for i = self.selection[prop] + 1, #self.selection do
  133. self.selection[self.selection[i]] = self.selection[self.selection[i]] - 1
  134. end
  135. table.remove(self.selection, self.selection[prop])
  136. self.selection[prop] = nil
  137. end
  138. return self:deselect(...)
  139. end
  140. function Selector:selectAll()
  141. self:select(unpack(ctx.map.props))
  142. end
  143. function Selector:deselectAll()
  144. self:deselect(unpack(ctx.map.props))
  145. end
  146. function Selector:each(fn)
  147. for i = 1, #self.selection do
  148. fn(self.selection[i])
  149. end
  150. end
  151. return Selector