container.lua 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  1. return function(BOXUI)
  2. local utils = BOXUI.utils
  3. local int = utils.int
  4. local max = utils.max
  5. local clamp = utils.clamp
  6. local newbox = utils.newbox
  7. local setbox = utils.setbox
  8. local includes = utils.includesPoint
  9. local colorWhite = {1, 1, 1}
  10. local M = utils.newclass("container")
  11. M.template_theme = {
  12. sliderThickness = 20, sliderLengthMin = 50,
  13. sliderBack = {0, 0.5, 0},
  14. sliderForeActive = {1, 0, 0}, sliderFore = {0.75, 0, 0},
  15. borderThickness = 8,
  16. borderBack = {0, 0, 0.5},
  17. borderFore = {0, 1, 1},
  18. handleHeight = 20
  19. }
  20. -- AREAS
  21. local units = {}
  22. M.units = units
  23. -- Vertical Slider
  24. units.SLIDER_V = newbox("sliderVert" )
  25. units.SLIDER_V.drag = function(owner, x, y, dx, dy)
  26. local s = owner.boxes.sliderVert
  27. if s.dir ~= 0 then return end -- sliding
  28. local a_max = owner.viewYMax
  29. local a = owner.viewY
  30. local s_space = s.height - s.length
  31. a = clamp(a + int(dy * a_max / s_space), 0, a_max)
  32. if a ~= owner.viewY then
  33. owner.viewY = a
  34. s.pos = int((s_space * a) / a_max)
  35. owner.content:updateViewport(owner.viewX, owner.viewY, owner.viewW, owner.viewH)
  36. owner:redraw()
  37. end
  38. end
  39. units.SLIDER_V.update = function(owner, dt)
  40. local s = owner.boxes.sliderVert
  41. if s.dir == 0 then return end
  42. owner:slideTo( 0, s.dir, dt)
  43. end
  44. -- Horizontal Slider
  45. units.SLIDER_H = newbox("sliderHori")
  46. units.SLIDER_H.drag = function(owner, x, y, dx, dy)
  47. local s = owner.boxes.sliderHori
  48. if s.dir ~= 0 then return end -- sliding
  49. local a_max = owner.viewXMax
  50. local a = owner.viewX
  51. local s_space = s.width - s.length
  52. a = clamp(a + int(dx * a_max / s_space), 0, a_max)
  53. if a ~= owner.viewX then
  54. owner.viewX = a
  55. s.pos = int((s_space * a) / a_max)
  56. owner.content:updateViewport(owner.viewX, owner.viewY, owner.viewW, owner.viewH)
  57. end
  58. end
  59. units.SLIDER_H.update = function(owner, dt)
  60. local s = owner.boxes.sliderHori
  61. if s.dir == 0 then return end
  62. owner:slideTo(s.dir, 0, dt)
  63. end
  64. -- Handle
  65. units.HANDLE = newbox("handle")
  66. units.HANDLE.drag = function(owner, x, y, dx, dy)
  67. local handle = owner.boxes.handle
  68. local parent = owner.parent
  69. local pw, ph
  70. if parent then
  71. local box = (parent.boxes and parent.boxes.panel) or parent
  72. pw, ph = box.width, box.height
  73. else
  74. pw, ph = love.graphics.getDimensions()
  75. end
  76. owner.x = clamp(dx + owner.x, 0 - owner.width + 20, pw - 20)
  77. owner.y = clamp(dy + owner.y, 0, ph - handle.height)
  78. end
  79. -- Handle Buttons
  80. units.SHADE = newbox("shade" )
  81. units.SHADE.click = function(owner) owner:rollup() end
  82. units.EXPAND = newbox("expand")
  83. units.EXPAND.click = function(owner) owner:maximize() end
  84. units.CLOSE = newbox("close" )
  85. units.CLOSE.click = function(owner)
  86. local parent = owner.parent
  87. if parent and parent.remove then
  88. parent:remove(parent:find(owner))
  89. end
  90. end
  91. -- Scalers
  92. units.SCALE_N = newbox("scaleN" )
  93. units.SCALE_NE = newbox("scaleNE")
  94. units.SCALE_NW = newbox("scaleNW")
  95. units.SCALE_E = newbox("scaleE" )
  96. units.SCALE_W = newbox("scaleW" )
  97. units.SCALE_SE = newbox("scaleSE")
  98. units.SCALE_SW = newbox("scaleSW")
  99. units.SCALE_S = newbox("scaleS" )
  100. local scale = function(owner, x, y, dx, dy, xdir, ydir)
  101. owner.scaling = true
  102. local handle = owner.boxes.handle
  103. local parent = owner.parent
  104. local pw, ph
  105. local minw, minh = 150, 150
  106. local threshold = 20
  107. if parent then
  108. local box = (parent.boxes and parent.boxes.panel) or parent
  109. pw, ph = box.width, box.height
  110. else
  111. pw, ph = love.graphics.getDimensions()
  112. end
  113. local ownermax_x, ownermax_y = owner.x + owner.width, owner.y + owner.height
  114. -- fixme: this might be simplified
  115. if xdir < 0 then
  116. local x = clamp(owner.x + dx, 0, ownermax_x - minw)
  117. if x < pw - threshold then
  118. owner.width = clamp(owner.width - dx, minw, ownermax_x)
  119. owner.x = x
  120. end
  121. elseif xdir > 0 and ownermax_x + dx >= threshold then
  122. owner.width = clamp(owner.width + dx, minw, pw - owner.x)
  123. end
  124. if ydir < 0 then
  125. local y = clamp(owner.y + dy, 0, ownermax_y - minh)
  126. if y < ph - threshold then
  127. owner.height = clamp(owner.height - dy, minh, ownermax_y)
  128. owner.y = y
  129. end
  130. elseif ydir > 0 and ownermax_y + dy >= threshold then
  131. owner.height = clamp(owner.height + dy, minh, ph - owner.y)
  132. end
  133. end
  134. units.SCALE_N.drag = function(owner, x, y, dx, dy) scale(owner, x, y, dx, dy, 0, -1) end
  135. units.SCALE_NE.drag = function(owner, x, y, dx, dy) scale(owner, x, y, dx, dy, 1, -1) end
  136. units.SCALE_NW.drag = function(owner, x, y, dx, dy) scale(owner, x, y, dx, dy, -1, -1) end
  137. units.SCALE_E.drag = function(owner, x, y, dx, dy) scale(owner, x, y, dx, dy, 1, 0) end
  138. units.SCALE_W.drag = function(owner, x, y, dx, dy) scale(owner, x, y, dx, dy, -1, 0) end
  139. units.SCALE_SE.drag = function(owner, x, y, dx, dy) scale(owner, x, y, dx, dy, 1, 1) end
  140. units.SCALE_SW.drag = function(owner, x, y, dx, dy) scale(owner, x, y, dx, dy, -1, 1) end
  141. units.SCALE_S.drag = function(owner, x, y, dx, dy) scale(owner, x, y, dx, dy, 0, 1) end
  142. local scaleEnd = function(owner)
  143. owner.scaling = false
  144. owner.maxWidth, owner.maxHeight = 0, 0
  145. owner:refresh(true) -- so force a rescale to owner.width, owner.height
  146. end
  147. units.SCALE_N.release, units.SCALE_S.release = scaleEnd, scaleEnd
  148. units.SCALE_E.release, units.SCALE_W.release = scaleEnd, scaleEnd
  149. units.SCALE_NE.release, units.SCALE_NW.release = scaleEnd, scaleEnd
  150. units.SCALE_SE.release, units.SCALE_SW.release = scaleEnd, scaleEnd
  151. -- Panel
  152. units.PANEL = newbox("panel")
  153. units.PANEL.drag = function(owner, x, y, dx, dy)
  154. local content = owner.content
  155. x, y = owner:contentRelativePos(owner.mouseX, owner.mouseY)
  156. content:mousemoved(x, y, dx, dy)
  157. end
  158. units.PANEL.release = function(owner, x, y, button)
  159. x, y = owner:contentRelativePos(x, y)
  160. owner.content:mousereleased(x, y, button)
  161. end
  162. units.PANEL.update = function(owner, dt)
  163. local content = owner.content
  164. local update = content.update
  165. if update then update(content, dt) end
  166. end
  167. ---------------- MODULE ----------------
  168. M.init = function(self, x, y, width, height)
  169. self.theme = BOXUI.theme
  170. self.x, self.y = x, y
  171. self.width, self.height = width, height
  172. self.activeArea = nil
  173. self.selectedArea = nil
  174. self.mouseX, self.mouseY = 0, 0 -- updated by mousemoved
  175. self.mousepresses = {} -- for non button 1 clicks on content
  176. -- viewport of content
  177. self.viewX = 0
  178. self.viewY = 0
  179. self.viewH = 0
  180. self.viewW = 0
  181. self.viewYMax = 0
  182. self.viewXMax = 0
  183. -- visibility of elements
  184. self.hasHandle = false
  185. self.hasSliderVert = true
  186. self.hasSliderHori = true
  187. self.hasClose = false
  188. self.hasExpand = false
  189. self.hasShade = false
  190. self.movable = false
  191. self.scalable = false
  192. self.sliderAdvance = 50
  193. self.sliderAdvanceTime = 0.050
  194. self.sliderAdvanceInit = 0.250
  195. self.content = nil
  196. self.wrapVert = false -- adjust height to content
  197. self.wrapHori = false -- adjust width to content
  198. self.slideDown = false -- after refresh
  199. self.title = "unknown"
  200. self.units = utils.deepcopy(M.units)
  201. self.buffered = true
  202. --self.canvas = love.graphics.newCanvas(width, height)
  203. self.maxWidth, self.maxHeight = width, height
  204. self.viewport = newbox("viewport", 0, 0, width, height)
  205. end
  206. --local dummyf = function() end
  207. --M.expand = dummyf
  208. --M.focus = dummyf
  209. -- called by parent to update viewport
  210. M.updateViewport = function(self, vpx, vpy, vpw, vph)
  211. setbox(self.viewport, vpx, vpy, vpw, vph)
  212. end
  213. -- redraw canvas
  214. M.redraw = function(self)
  215. local canvas = self.canvas
  216. if not canvas then return end
  217. love.graphics.setCanvas(canvas)
  218. love.graphics.clear()
  219. self:drawPassive(0, 0)
  220. love.graphics.setCanvas()
  221. end
  222. -- draw either canvas or dynamically
  223. M.draw = function(self, x, y)
  224. local theme = self.theme
  225. local vp = self.viewport
  226. x, y = self.x - vp.x + (x or 0), self.y - vp.y + (y or 0)
  227. if self.scaling then
  228. local w, h = self.width, self.height
  229. local bt = theme.borderThickness
  230. love.graphics.setColor(theme.borderBack)
  231. love.graphics.rectangle('fill', x, y, w, h)
  232. love.graphics.setColor(theme.borderFore)
  233. love.graphics.setLineWidth(bt)
  234. love.graphics.rectangle('line', x + bt / 2, y + bt / 2, w - bt, h - bt)
  235. love.graphics.print(("%i x %i"):format(w, h), x + bt, y + bt)
  236. return
  237. end
  238. if self.canvas then
  239. love.graphics.setColor(colorWhite)
  240. love.graphics.draw(self.canvas, x, y)
  241. else
  242. self:drawPassive(x, y)
  243. end
  244. self:drawActive(x, y)
  245. end
  246. -- create a new canvas if required, return max dimensions
  247. M.fit = function(self)
  248. local selfw, selfh = self.width, self.height
  249. local canvas = self.canvas
  250. local buffered = self.buffered
  251. if buffered and not canvas then
  252. self.canvas = love.graphics.newCanvas(selfw, selfh)
  253. self.maxWidth, self.maxHeight = selfw, selfh
  254. return selfw, selfh
  255. end
  256. local width, height = self.maxWidth, self.maxHeight
  257. if selfw <= width and selfh <= height then return width, height end
  258. if canvas then canvas:release() end
  259. if buffered then
  260. self.canvas = love.graphics.newCanvas(selfw, selfh)
  261. end
  262. self.maxWidth, self.maxHeight = selfw, selfh
  263. return selfw, selfh
  264. end
  265. M.includes = includes
  266. M.refresh = function(self, redraw)
  267. self.width, self.height = self:fit()
  268. self.activeArea = nil
  269. self.selectedArea = nil
  270. local theme = self.theme
  271. local st = theme.sliderThickness
  272. local bt = theme.borderThickness
  273. local hh = self.hasHandle and theme.handleHeight or 0
  274. local panelw = self.width - 2 * bt
  275. local panelh = self.height - 2 * bt - hh
  276. local content = self.content
  277. if content.refresh then content:refresh() end
  278. local cwidth, cheight = content.width, content.height
  279. local stvert = self.hasSliderVert and st or 0
  280. local sthori = self.hasSliderHori and st or 0
  281. local vert, hori -- overflow check
  282. if cheight > panelh then
  283. vert = true
  284. panelw = panelw - stvert
  285. end
  286. if cwidth > panelw then
  287. hori = true
  288. panelh = panelh - sthori
  289. if not vert and cheight > panelh then
  290. vert = true
  291. panelw = panelw - stvert
  292. end
  293. end
  294. if vert then
  295. self.viewYMax = cheight - panelh
  296. else
  297. self.viewYMax = 0
  298. stvert = 0
  299. end
  300. if hori then
  301. self.viewXMax = cwidth - panelw
  302. else
  303. self.viewXMax = 0
  304. sthori = 0
  305. end
  306. if self.wrapVert and not vert then
  307. panelh = cheight
  308. self.height = panelh + 2 * bt + sthori + hh
  309. end
  310. if self.wrapHori and not hori then
  311. panelw = cwidth
  312. self.width = panelw + 2 * bt + stvert
  313. end
  314. if content.expand and (panelw > cwidth or panelh > cheight) then
  315. content:expand(panelw, panelh)
  316. end
  317. --content.x, content.y = bt, bt + hh
  318. content.parent = self
  319. if content.redraw then content:redraw() end
  320. cwidth, cheight = content.width, content.height
  321. -- compute boxes fixme: move to theme.lua?
  322. local u = self.units
  323. local boxes = {}
  324. local collidables = {}
  325. self.boxes = boxes
  326. self.collidables = collidables
  327. local box
  328. if self.scalable then
  329. box = setbox(u.SCALE_SE, panelw + stvert, hh + panelh + sthori, 2 * bt, 2 * bt)
  330. boxes[box.name] = box; table.insert(collidables, box)
  331. box = setbox(u.SCALE_NW, 0, 0, 2 * bt, 2 * bt)
  332. boxes[box.name] = box; table.insert(collidables, box)
  333. end
  334. if self.hasHandle then
  335. local i = 1
  336. local handlew = panelw + stvert
  337. local hbw, hbh = theme.handleButtonWidth, theme.handleButtonHeight
  338. local hbx = bt + handlew
  339. local hby = bt + int((hh - hbh) / 2)
  340. local hbadvance = theme.handlePadVert + hbw
  341. if self.hasClose then
  342. hbx = hbx - hbadvance
  343. box = setbox(u.CLOSE, hbx, hby, hbw, hbh)
  344. boxes[box.name] = box; table.insert(collidables, box)
  345. end
  346. if self.hasShade then
  347. hbx = hbx - hbadvance
  348. box =setbox(u.SHADE, hbx, hby, hbw, hbh)
  349. boxes[box.name] = box; table.insert(collidables, box)
  350. end
  351. if self.hasExpand then
  352. hbx = hbx - hbadvance
  353. box = setbox(u.EXPAND, hbx, hby, hbw, hbh)
  354. boxes[box.name] = box; table.insert(collidables, box)
  355. end
  356. box = setbox(u.HANDLE, bt, bt, handlew, hh)
  357. boxes[box.name] = box
  358. if self.movable then table.insert(collidables, box) end
  359. end
  360. box = setbox(u.PANEL, bt, bt + hh, panelw, panelh)
  361. boxes[box.name] = box; table.insert(collidables, box)
  362. if vert and self.hasSliderVert then
  363. box = setbox(u.SLIDER_V, bt + panelw, bt + hh, st, panelh)
  364. box.length = max(theme.sliderLengthMin, int(panelh * (panelh / cheight)))
  365. box.pos = self.slideDown and (panelh - box.length) or 0
  366. box.dir = 0
  367. boxes[box.name] = box; table.insert(collidables, box)
  368. end
  369. if hori and self.hasSliderHori then
  370. box = setbox(u.SLIDER_H, bt, bt + hh + panelh, panelw, st)
  371. box.length = max(theme.sliderLengthMin, int(panelw * (panelw / cwidth)))
  372. box.pos, box.dir = 0, 0
  373. boxes[box.name] = box; table.insert(collidables, box)
  374. end
  375. self.viewX, self.viewY = 0, (self.slideDown and self.viewYMax or 0)
  376. self.viewW, self.viewH = (hori and panelw or cwidth), (vert and panelh or cheight)
  377. content:updateViewport(self.viewX, self.viewY, self.viewW, self.viewH)
  378. self.drawActive = self.theme.draw.containerActive
  379. self.drawPassive = self.theme.draw.containerPassive
  380. if redraw then self:redraw() end
  381. end
  382. M.selectActiveArea = function(self, x, y)
  383. local prevActive = self.activeArea
  384. local active = nil
  385. x, y = x - self.x, y - self.y
  386. local boxes = self.collidables
  387. for i, v in ipairs(boxes) do
  388. if not v.hidden and includes(v, x, y) then
  389. active = v
  390. break
  391. end
  392. end
  393. boxes = self.boxes
  394. local box = active
  395. if box then
  396. if box == boxes.sliderVert then
  397. local pos, boxpos = y - box.y, box.pos
  398. if pos < boxpos then box.dir = -1
  399. elseif pos >= boxpos + box.length then box.dir = 1
  400. else box.dir = 0 end
  401. elseif box == boxes.sliderHori then
  402. local pos, boxpos = x - box.x, box.pos
  403. if pos < boxpos then box.dir = -1
  404. elseif pos >= boxpos + box.length then box.dir = 1
  405. else box.dir = 0 end
  406. end
  407. end
  408. self.activeArea = active
  409. if prevActive ~= active then
  410. local content = self.content
  411. if prevActive == boxes.panel and content.unfocus then content:unfocus()
  412. elseif active == boxes.panel and content.focus then content:focus() end
  413. end
  414. end
  415. M.contentRelativePos = function(self, x, y)
  416. local panel = self.boxes.panel
  417. return x - self.x - panel.x + self.viewX, y - self.y - panel.y + self.viewY
  418. end
  419. M.unfocus = function(self)
  420. local active = self.activeArea
  421. if not active then return end
  422. local content = self.content
  423. if active == self.boxes.panel and content.unfocus then content:unfocus() end
  424. self.activeArea = nil
  425. end
  426. M.wheelmoved = function(self, wx, wy)
  427. local x, y = self.mouseX, self.mouseY
  428. --if not self:includes(x, y) then return end
  429. local content = self.content
  430. local cwm = content.wheelmoved
  431. if self.activeArea == self.boxes.panel and cwm and cwm(content, wx, wy) then
  432. x, y = self:contentRelativePos(x, y)
  433. content:mousemoved(x, y, 0, 0)
  434. return
  435. end
  436. local viewymax = self.viewYMax
  437. if viewymax <= 0 then
  438. return
  439. end -- nothing to scroll
  440. local viewy = int(self.viewY - wy * self.sliderAdvance)
  441. viewy = clamp(viewy, 0, viewymax)
  442. if viewy == self.viewY then
  443. return
  444. end
  445. self.viewY = viewy
  446. local s = self.boxes.sliderVert
  447. if s then
  448. s.pos = int((s.height - s.length) * (viewy / viewymax))
  449. end
  450. content:updateViewport(self.viewX, self.viewY, self.viewW, self.viewH)
  451. return true
  452. end
  453. M.mousemoved = function(self, x, y, dx, dy)
  454. self.mouseX, self.mouseY = x, y
  455. local selected = self.selectedArea
  456. local drag = selected and selected.drag
  457. local content = self.content
  458. if drag then
  459. self.dragging = true
  460. --if x > 0 and y > 0 then -- fixme: needed for scaling
  461. drag(self, x, y, dx, dy)
  462. --end
  463. return
  464. end
  465. if not self:includes(x, y) then return self:unfocus() end
  466. self:selectActiveArea(x, y)
  467. if self.activeArea == self.boxes.panel then
  468. x, y = self:contentRelativePos(x, y)
  469. content:mousemoved(x, y, dx, dy)
  470. end
  471. end
  472. M.mousepressed = function(self, x, y, button)
  473. self.dt = 0
  474. self.advancing = false
  475. if not self:includes(x, y) then return end
  476. if button == 1 then
  477. self.selectedArea = self.activeArea
  478. end
  479. if self.activeArea == self.boxes.panel then
  480. --print(self.id, "pressed", button, self.content.id)
  481. x, y = self:contentRelativePos(x, y)
  482. self.content:mousepressed(x, y, button)
  483. self.mousepresses[button] = true
  484. end
  485. end
  486. local shade_list = {
  487. panel = true, sliderVert = true, sliderHori = true,
  488. scaleN = true, scaleNW = true, scaleW = true, scaleSW = true,
  489. scaleS = true, scaleSE = true, scaleE = true, scaleNE = true,
  490. --close = false, expand = false, shade = false, handle = false,
  491. }
  492. M.rollup = function(self, shade)
  493. if shade == nil then shade = not self.shaded end
  494. local boxes = self.boxes
  495. local handle = boxes.handle
  496. if shade then
  497. self.shadedHeight = self.height
  498. self.height = handle.height + 2 * handle.x
  499. else
  500. self.height = self.shadedHeight
  501. end
  502. local s
  503. for k, v in pairs(shade_list) do
  504. s = boxes[k]
  505. if v and s then s.hidden = shade end
  506. end
  507. self.shaded = shade
  508. self:redraw()
  509. end
  510. local premaxvars = {x = true, y = true, width = true, height = true,
  511. maxWidth = true, maxHeight = true,
  512. canvas = true,
  513. wrapVert = true, wrapHori = true,
  514. scalable = true, movable = true}
  515. M.maximize = function(self, maximize)
  516. if self.shaded then self:rollup(false) end
  517. if maximize == nil then maximize = not self.maximized end
  518. local old
  519. if maximize then
  520. old = {}
  521. self.preMaximize = old
  522. for k, v in pairs(premaxvars) do
  523. old[k] = self[k]
  524. end
  525. local minx, miny, maxw, maxh
  526. local parent = self.parent
  527. if parent then
  528. local box = (parent.boxes and parent.boxes.panel) or parent
  529. minx, miny = box.x, box.y
  530. maxw, maxh = box.width, box.height
  531. else
  532. minx, miny = 0, 0
  533. maxw, maxh = love.graphics.getDimensions()
  534. end
  535. self.x, self.y = minx, miny
  536. self.width, self.height = maxw, maxh
  537. self.maxWidth, self.maxHeight = 0, 0 -- force rescale during refresh -> fit
  538. self.scalable, self.movable = false, false
  539. self.wrapVert, self.wrapHori = false, false
  540. else
  541. old = self.preMaximize
  542. for k, v in pairs(premaxvars) do
  543. self[k] = old[k]
  544. end
  545. self.width, self.height = self.maxWidth, self.maxHeight
  546. self.maxWidth, self.maxHeight = 0, 0 -- force rescale during refresh -> fit
  547. end
  548. self.maximized = maximize
  549. self:refresh(true)
  550. end
  551. M.mousereleased = function(self, x, y, button)
  552. if button ~=1 then
  553. if self.mousepresses[button] then
  554. --print(self.id, "release", button, self.content.id)
  555. x, y = self:contentRelativePos(x, y)
  556. self.content:mousereleased(x, y, button)
  557. end
  558. return
  559. end
  560. local selected = self.selectedArea
  561. if selected == nil then return end
  562. self.selectedArea = nil
  563. if self.dragging then
  564. self.dragging = false
  565. local release = selected.release -- (drag) release
  566. if release then release(self, x, y, button) end
  567. if not self:includes(x, y) then return self:unfocus() end
  568. self:selectActiveArea(x, y)
  569. if self.activeArea == self.boxes.panel then
  570. x, y = self:contentRelativePos(x, y)
  571. self.content:mousemoved(x, y, 0, 0)
  572. end
  573. return
  574. end
  575. local click = selected.click
  576. if click and self.activeArea == selected then return click(self) end
  577. if selected == self.boxes.panel then
  578. x, y = self:contentRelativePos(x, y)
  579. self.content:mousereleased(x, y, button)
  580. end
  581. end
  582. M.slideTo = function(self, xdir, ydir, dt)
  583. self.dt = self.dt + dt
  584. if not self.advancing then
  585. if self.dt < self.sliderAdvanceInit then return end
  586. self.advancing = true; self.dt = self.dt - self.sliderAdvanceInit
  587. end
  588. if self.dt < self.sliderAdvanceTime then return end
  589. self.dt = self.dt - self.sliderAdvanceTime
  590. local boxes = self.boxes
  591. local panel = boxes.panel
  592. local x, y = self.mouseX, self.mouseY
  593. x, y = x - self.x - panel.x, y - self.y - panel.y
  594. local s, a, a_len, a_max, targetpos
  595. dt = self.sliderAdvance -- constant time
  596. local changed = false
  597. if xdir ~= 0 then
  598. a = int(self.viewX + xdir * dt)
  599. a_len = self.viewW
  600. a_max = self.viewXMax
  601. targetpos = int(clamp(x + (x * a_max) / a_len - a_len / 2, 0, a_max))
  602. if xdir * (a - targetpos) > 0 then a = targetpos end
  603. if a ~= self.viewX then
  604. self.viewX = a
  605. s = boxes.sliderHori
  606. if s then
  607. s.pos = int(((s.width - s.length) * a) / a_max)
  608. if a == targetpos then s.dir = 0 end
  609. end
  610. changed = true
  611. end
  612. end
  613. if ydir ~= 0 then
  614. a = int(self.viewY + ydir * dt) -- fixme use trim
  615. a_len = self.viewH
  616. a_max = self.viewYMax
  617. targetpos = int(clamp(y + (y * a_max) / a_len - a_len / 2, 0, a_max))
  618. if ydir * (a - targetpos) > 0 then a = targetpos end
  619. if a ~= self.viewY then
  620. self.viewY = a
  621. s = boxes.sliderVert
  622. if s then
  623. s.pos = int(((s.height - s.length) * a) / a_max)
  624. if a == targetpos then s.dir = 0 end
  625. end
  626. changed = true
  627. end
  628. end
  629. if changed then
  630. self.content:updateViewport(self.viewX, self.viewY, self.viewW, self.viewH)
  631. end
  632. end
  633. M.update = function(self, dt)
  634. local active, selected = self.activeArea, self.selectedArea
  635. if not selected or selected ~= active or not selected.update then return end
  636. selected.update(self, dt)
  637. end
  638. BOXUI.add(M)
  639. end