local player = require'cplayer' local box = require'box2d' local function set_hierarchy(items) for i,item in ipairs(items) do item.index = i item.parent = items end end local function set_sizes(items) items._w = 0 items._h = 0 for i, item in ipairs(items) do if items.vert then item._w = math.max(items.min_w or 0, items.w or 0) item._h = math.max(item.min_h or 0, item.h or 0, items.min_h or 0, items.item_h or 0) items._h = items._h + item._h items._w = math.max(items._w, item._w) else item._h = math.max(items.min_h or 0, items.h or 0) item._w = math.max(item.min_w or 0, item.w or 0, items.min_w or 0, items.item_w or 0) items._w = items._w + item._w items._h = math.max(items._h, item._h) end end end local function set_layout(items) items._x = items.x items._y = items.y local x, y = items._x, items._y for i,item in ipairs(items) do item._x = x item._y = y if items.vert then y = y + item._h else x = x + item._w end end end local function set_all(items) set_hierarchy(items) set_sizes(items) set_layout(items) end local function remove_item(item) assert(item.parent[item.index] == item) table.remove(item.parent, item.index) set_layout(item.parent) item.parent = nil item.index = nil end local function insert_item(item, items, index) table.insert(items, index, item) set_layout(items) end local function set_drag_layout(items, drag_item, x, y) drag_item._x = x drag_item._y = y if drag_item.parent == items then for i = drag_item.index + 1, #items do local ditem = items[i] if items.vert then ditem._y = ditem._y - drag_item._h else ditem._x = ditem._x - drag_item._w end end end --make room for drag_item in layout and return the position where it should be inserted local drop_index for i,item in ipairs(items) do if item ~= drag_item then if items.vert then if not drop_index and item._y + item._h / 2 > drag_item._y and drag_item._x + drag_item._w > items._x and drag_item._x < items._x + items._w then drop_index = i-1 end if drop_index then item._y = item._y + drag_item._h end else if not drop_index and item._x + item._w / 2 > drag_item._x and drag_item._y + items._h > items._y and drag_item._y < items._y + items._h then drop_index = i-1 end if drop_index then item._x = item._x + drag_item._w end end end end return drop_index or #items + (drag_item.parent ~= items and 1 or 0) end local function move_item(item, items, i) assert(i >= 1 and i <= #items + (item.parent ~= items and 1 or 0)) table.remove(item.parent, item.index) set_layout(item.parent) table.insert(items, i, item) set_layout(items) end local function item_box(item) return item._x, item._y, item._w, item._h end local function hit_test(mx, my, items) for i,item in ipairs(items) do if box.hit(mx, my, item_box(item)) then return item end end end function player:itemlist(items) local id = assert(items.id, 'id missing') local x, y, w, h = self:getbox(items) set_all(items) local mx, my = self:mousepos() if self.active == id or (self.active and self.ui.item) then if self.lbutton then self.ui.drop_index = set_drag_layout(items, self.ui.item, mx - self.ui.dx, my - self.ui.dy) self.ui.drop_itemlist = items elseif self.active == id then if self.ui.drop_index and self.ui.drop_itemlist == items then insert_item(self.ui.item, items, self.ui.drop_index) self:invalidate() end self.active = nil self.ui.item = nil end else local item = hit_test(mx, my, items) if item then self.cursor = 'hand' if not self.active and self.lbutton then self.active = id self.ui.dx = mx - item._x self.ui.dy = my - item._y self.ui.item = item remove_item(item) self:invalidate() end end end for i,item in ipairs(items) do local x, y, w, h = item_box(item) self:rect(x, y, w, h, 'normal_bg', 'normal_fg') end if self.ui.item then local x, y, w, h = item_box(self.ui.item) self:rect(x, y, w, h, 'normal_bg', 'normal_fg') end end if not ... then local player = require'cplayer' local glue = require'glue' local items = { id = 'items', x = 10, y = 10, w = 400, h = 40, vert = false, {w = 50, h = 100}, {w = 100, h = 50}, {w = 20, h = 100}, {w = 70, h = 20}, {w = 50, h = 100}, } local items2 = { id = 'items2', x = 500, y = 10, w = 400, h = 400, vert = true, {w = 50, h = 100}, {w = 100, h = 50}, {w = 20, h = 100}, {w = 70, h = 20}, {w = 50, h = 100}, } player.continuous_rendering = false function player:on_render(cr) cr:translate(0.5, 0.5) self:itemlist(items) self:itemlist(items2) end player:play() end