クラスを使う 5, 任意のオブジェクトをクリックして選択する

-- 任意のオブジェクトをクリックして選択する
-- 参考: http://compasstech.com.au/TNS_Authoring/Scripting/script_tut11.html ~ tut15.html


require "color"

W = platform.window:width()
H = platform.window:height()

TrackedObject = nil
TrackOffsetx = 0
TrackOffsety = 0

Square = class()
function Square:init(x, y, sideLen, color)
   self.x           = x
   self.y           = y
   self.sideLen     = sideLen
   self.color       = color
   self.selected    = false
end
function Square:contains(x, y)
   return isInside(self.x, x, self.x + self.sideLen) and
          isInside(self.y, y, self.y + self.sideLen)
end
function Square:move(dx, dy)
   self.x = median(0, self.x + dx, W - self.sideLen)
   self.y = median(0, self.y + dy, H - self.sideLen)
end
function Square:paint(gc)
   gc:setColorRGB(self.color)
   gc:fillRect(self.x, self.y, self.sideLen, self.sideLen, self.color)
   if self.selected then
      gc:setColorRGB(color.black)
      gc:setPen("thick")
      gc:drawRect(self.x, self.y, self.sideLen, self.sideLen)
   end
end



Objects = {
   Square(10, 80, 50, color.brown),
   Square(70, 80, 50, color.red),
   Square(130, 80, 50, color.orange),
   Square(190, 80, 50, color.yellow),
   Square(250, 80, 50, color.green)
}


function on.mouseDown(x, y)
   for i = 1, #Objects do                   -- オブジェクトリストを左から順に走査して、
      if Objects[i]:contains(x, y) then     -- 包含判定の true と出た要素が見つかったら、
         if TrackedObject then              -- 旧「注目オブジェクト」の
            TrackedObject.selected = false  -- 選択状態を解除して、(旧「注目オブジェクト」が空の場合は何もしない)
         end
         TrackedObject = Objects[i]         -- 包含判定の true と出た要素を新「注目オブジェクト」にセットして、
         TrackedObject.selected = true      -- 新「注目オブジェクト」を選択状態にセットする。
         TrackOffsetx = x - TrackedObject.x -- オブジェクトの描画始点以外がつかめるよう、クリック座標と描画始点との距離を記憶しておく(今回は使っていない)。
         TrackOffsety = y - TrackedObject.y -- オブジェクトの描画始点以外がつかめるよう、クリック座標と描画始点との距離を記憶しておく(今回は使っていない)。
         break                              -- 包含判定の true と出た要素が見つかったらそれ以上はループしない。
      end
   end
platform.window:invalidate()
end
function on.mouseUp()                -- 左マウスボタンをリリースしたら、
   if TrackedObject then             -- 「注目オブジェクト」の
      TrackedObject.selected = false -- 選択状態を解除する(「注目オブジェクト」が空の場合は何もしない)。
   end
platform.window.invalidate()
end
function on.arrowKey(key)
   if TrackedObject then
      if     key == "up"    then TrackedObject:move( 0, -5)
      elseif key == "down"  then TrackedObject:move( 0,  5)
      elseif key == "left"  then TrackedObject:move(-5,  0)
      elseif key == "right" then TrackedObject:move( 5,  0)
      end
   end
end
function on.tabKey()
   if TrackedObject then
      TrackedObject.selected = false
   end
   for i = 1, #Objects do
      if Objects[i] == TrackedObject then
         TrackedObject = Objects[i+1]
         break
      end
   end
   if TrackedObject == nil then
       TrackedObject = Objects[1]
   end
   TrackedObject.selected = true
   platform.window:invalidate()
end
function on.backtabKey()
   if TrackedObject then
      TrackedObject.selected = false
   end
   for i = #Objects, 1, -1 do
      if Objects[i] == TrackedObject then
         TrackedObject = Objects[i-1]
         break
      end
   end
   if TrackedObject == nil then
       TrackedObject = Objects[#Objects]
   end
   TrackedObject.selected = true
   platform.window:invalidate()
end
function on.escapeKey()
   if TrackedObject then
      TrackedObject.selected = false
   end
   TrackedObject = nil
   platform.window:invalidate()
end
function on.paint(gc)
   for i = 1, #Objects do
      Objects[i]:paint(gc)
   end
end



-----------------------
-- general functions --
-----------------------
-- 中央値函数(下限値, 可変値, 上限値)
function median(lowerLimit, x, upperLimit) return math.max(lowerLimit, math.min(x, upperLimit)) end
-- 包含判定函数(下限値, 可変値, 上限値)
function isInside(lowerLimit, x, upperLimit) return lowerLimit <= x and x <= upperLimit end