Pop.Box/elements/window.moon

312 lines
13 KiB
Plaintext
Raw Normal View History

--- A generic window element. Built-in support for minimize, maximize, and close
--- buttons, as well as drag-to-resize and drag-to-move. Title bar customizable.
2016-08-23 04:22:13 +00:00
--- @classmod window
--- @copyright Paul Liverman III (2016)
--- @license The MIT License (MIT)
2017-04-30 23:40:29 +00:00
--- @todo Implement missing features.
2016-08-23 04:22:13 +00:00
2017-04-30 23:40:29 +00:00
local pop
2017-05-01 01:01:19 +00:00
import graphics, mouse from love
2017-04-30 23:40:29 +00:00
path = (...)\sub 1, -7
element = require "#{path}/element"
import inheritsFromElement from require "#{path\sub 1, -11}/util"
2017-05-01 01:01:19 +00:00
path = path\sub 1, -11
maximizeImage = graphics.newImage "#{path}/images/maximize.png"
minimizeImage = graphics.newImage "#{path}/images/minimize.png"
closeImage = graphics.newImage "#{path}/images/close.png"
-- drag to resize is based on area of padding, which defaults to 5 pixels
class window extends element
2017-04-30 23:40:29 +00:00
load: (pop_lib) ->
pop = pop_lib
2016-08-23 04:22:13 +00:00
--- Constructor expects nothing, or a data table describing it.
2017-04-30 23:40:29 +00:00
--- @todo document containMethod values
new: (@parent, @data={}, title="Window") =>
super @parent, @data
@data.type = "window" if @data.type == "element"
2017-04-30 23:40:29 +00:00
@data.w = 100 unless @data.w > 0
@data.h = 80 unless @data.h > 0
2017-05-01 01:01:19 +00:00
-- how a window is contained within its parent element
2017-04-30 23:40:29 +00:00
@data.containMethod = "mouse" unless @data.containMethod
2017-05-01 01:01:19 +00:00
@data.maximized = false
@data.titleBar = true if @data.titleBar == nil
@data.moveable = true if @data.moveable == nil
2017-05-01 01:01:19 +00:00
@data.maximizeable = false if @data.maximizeable == nil
@data.minimizeable = false if @data.minimizeable == nil
@data.closeable = false if @data.closeable == nil
unless @data.previous
@data.previous = {}
@header = pop.box @, {type: "box (window header)"}, @data.titleBackground or {25, 180, 230, 255}
@title = pop.text @header, {horizontal: "center", type: "text (window title)"}, title, @data.titleColor or {255, 255, 255, 255}
@window_area = pop.box @, {padding: 5, type: "box (window area)"}, @data.windowBackground or {200, 200, 210, 255}
2017-05-01 01:01:19 +00:00
-- buttons! :D
@data.header_width_reduction = 0
buttonSize = @title\getHeight! + 1
if @data.closeable
@closeButton = pop.box(@, {w: buttonSize, h: buttonSize, horizontalMargin: @data.header_width_reduction, type: "box (window close button)"}, closeImage)\align "right"
2017-05-01 01:01:19 +00:00
@closeButton.clicked = (x, y, button) =>
if button == pop.constants.left_mouse
@parent\close!
2017-08-14 01:34:51 +00:00
return true
2017-05-01 01:01:19 +00:00
@data.header_width_reduction += buttonSize
if @data.maximizeable
@maximizeButton = pop.box(@, {w: buttonSize, h: buttonSize, horizontalMargin: @data.header_width_reduction, type: "box (window maximize button)"}, maximizeImage)\align "right"
2017-05-01 01:01:19 +00:00
@maximizeButton.clicked = (x, y, button) =>
if button == pop.constants.left_mouse
@parent\maximize!
2017-08-14 01:34:51 +00:00
return true
2017-05-01 01:01:19 +00:00
@data.header_width_reduction += buttonSize
if @data.minimizeable
@minimizeButton = pop.box(@, {w: buttonSize, h: buttonSize, horizontalMargin: @data.header_width_reduction, type: "box (window minimize button)"}, minimizeImage)\align "right"
2017-05-01 01:01:19 +00:00
@minimizeButton.clicked = (x, y, button) =>
if button == pop.constants.left_mouse
@parent\minimize!
2017-08-14 01:34:51 +00:00
return true
2017-05-01 01:01:19 +00:00
@data.header_width_reduction += buttonSize
height = @title\getHeight! + 1
@header\setSize @data.w - @data.header_width_reduction, height
if @data.titleBar
@window_area\setSize @data.w, @data.h - height
@window_area\move nil, height
else
@header.data.draw = false
@window_area.data.x = @data.x + @data.padding
@window_area.data.y = @data.y + @data.padding
@window_area.data.w = @data.w - @data.padding*2
@window_area.data.h = @data.h - @data.padding*2
2017-04-30 23:40:29 +00:00
-- window area steals mouse events to prevent propagation to elements visibily underneath it (not within its hierarchy)
2017-05-01 01:01:19 +00:00
@window_area.mousepressed = (x, y, button) =>
2017-04-30 23:40:29 +00:00
if button == pop.constants.left_mouse
grandparent = @parent.parent
table.insert grandparent.child, table.remove(grandparent.child, grandparent\indexOf @parent)
return nil
2017-05-01 01:01:19 +00:00
@window_area.clicked = =>
2017-04-30 23:40:29 +00:00
return nil
-- @window_area.add = (element) =>
-- pop.elements.box.__parent.add @, element
-- --NOTE temporarily disabled
-- -- this seems to be working but errors with previous == nil
-- -- I have moved the intended functionality to the window element's function that calls these
-- if false and inheritsFromElement element
-- -- we need to adjust its position based on the previous element
-- previous = @data.child[#@data.child-1]
-- y, h = previous.y, previous.h
-- y += h + @data.padding
-- element.data.x = @data.x + @data.padding
-- element.data.y = y
-- element\setWidth @data.w - @data.padding*2
--
-- @window_area.remove = (element) =>
-- pop.elements.box.__parent.remove @, element
-- --TODO we need to adjust all elements' positions
2017-05-01 20:45:04 +00:00
2017-04-30 23:40:29 +00:00
selected = false
mx = 0
my = 0
@header.mousemoved = (x, y, dx, dy) =>
if selected
@parent\move dx, dy
-- do not leave area of grandparent (based on containMethod)
grandparent = @parent.parent
switch @parent.data.containMethod
when "title" -- the window title can't leave
@parent\move(grandparent.data.x - @data.x) if @data.x < grandparent.data.x
@parent\move(nil, grandparent.data.y - @data.y) if @data.y < grandparent.data.y
@parent\move(grandparent.data.x + grandparent.data.w - (@data.x + @data.w)) if @data.x + @data.w > grandparent.data.x + grandparent.data.w
@parent\move(nil, grandparent.data.y + grandparent.data.h - (@data.y + @data.h)) if @data.y + @data.h > grandparent.data.y + grandparent.data.h
when "body" -- the entire window can't leave
@parent\move(grandparent.data.x - @data.x) if @data.x < grandparent.data.x
@parent\move(nil, grandparent.data.y - @data.y) if @data.y < grandparent.data.y
@parent\move(grandparent.data.x + grandparent.data.w - (@parent.data.x + @parent.data.w)) if @parent.data.x + @parent.data.w > grandparent.data.x + grandparent.data.w
@parent\move(nil, grandparent.data.y + grandparent.data.h - (@parent.data.y + @parent.data.h)) if @parent.data.y + @parent.data.h > grandparent.data.y + grandparent.data.h
when "mouse" -- wherever the mouse has clicked can't leave
@parent\setPosition(grandparent.data.x + @data.w - mx) if mouse.getX! < grandparent.data.x
@parent\setPosition(nil, grandparent.data.y + @parent.data.h - my) if mouse.getY! < grandparent.data.y
@parent\setPosition(grandparent.data.x + grandparent.data.w + @data.w - mx) if mouse.getX! > grandparent.data.x + grandparent.data.w
@parent\setPosition(nil, grandparent.data.y + grandparent.data.h + @parent.data.h - my) if mouse.getY! > grandparent.data.y + grandparent.data.h
return true
return false
@header.mousepressed = (x, y, button) =>
2017-08-14 02:31:29 +00:00
if button == pop.constants.left_mouse and @parent.data.moveable
2017-04-30 23:40:29 +00:00
grandparent = @parent.parent
table.insert grandparent.child, table.remove(grandparent.child, grandparent\indexOf @parent)
selected = true
mx = x
my = y
return true
return false
@header.mousereleased = (x, y, button) =>
if button == pop.constants.left_mouse
selected = false
if @ == pop.focused
pop.focused = false
2017-04-30 23:40:29 +00:00
return true
return false
2017-08-28 08:17:54 +00:00
if @data.size != 0 or @data.verticalSize != 0 or @data.horizontalSize != 0
@setSize @parent.data.w * (@data.size + @data.verticalSize), @parent.data.h * (@data.size + @data.horizontalSize)
elseif @data.align
@align!
2017-04-30 23:40:29 +00:00
align: (...) =>
unless @data.align return @
super ...
@header\align!
@title\align!
@window_area\align!
2017-05-01 01:01:19 +00:00
if @closeButton
@closeButton\align!
if @maximizeButton
@maximizeButton\align!
if @minimizeButton
@minimizeButton\align!
2017-04-30 23:40:29 +00:00
if @data.titleBar
@window_area\move nil, @header\getHeight!
2017-04-30 23:40:29 +00:00
return @
setSize: (w, h) =>
x = 0
y = 0
if w
switch @data.horizontalAlign
when "left"
local _ -- do nothing
2017-04-30 23:40:29 +00:00
when "center"
x -= (w - @data.w) / 2
when "right"
x -= w - @data.w
else
if @data.horizontalAlign < 0
x -= w - @data.w
2017-04-30 23:40:29 +00:00
2017-05-01 01:01:19 +00:00
@header\setWidth w - @data.header_width_reduction
2017-04-30 23:40:29 +00:00
@window_area\setWidth w
@data.w = w
@data.x += x
@title\align!
if h
switch @data.verticalAlign
when "top"
local _ -- do nothing
2017-04-30 23:40:29 +00:00
when "center"
y -= (h - @data.h) / 2
when "bottom"
2017-04-30 23:40:29 +00:00
y -= h - @data.h
else
if @data.horizontalAlign < 0
y -= h - @data.h
2017-04-30 23:40:29 +00:00
if @data.titleBar
@window_area\setHeight h - @header\getHeight!
@window_area\move nil, @header\getHeight!
else
@window_area\setHeight h
2017-04-30 23:40:29 +00:00
@data.h = h
@data.y += y
@header\move x, y
@window_area\move x, y
return @
2017-04-30 23:40:29 +00:00
setWidth: (w) =>
return @setSize w
2016-08-23 04:22:13 +00:00
2017-04-30 23:40:29 +00:00
setHeight: (h) =>
return @setSize nil, h
2017-05-01 01:01:19 +00:00
setPadding: (padding) =>
@window_area\setPadding padding
return @
getPadding: =>
return @window_area\getPadding!
add: (element) =>
@window_area\add element
x, y = @window_area.data.x, @window_area.data.y
--for element in *@window_area.child
--element\setWidth @data.w - @data.padding*2
--TODO needs to align them vertically with appropriate padding (I am not taking margin into account which is bad)
return @
-- @window_area.add = (element) =>
-- pop.elements.box.__parent.add @, element
-- --NOTE temporarily disabled
-- -- this seems to be working but errors with previous == nil
-- -- I have moved the intended functionality to the window element's function that calls these
-- if false and inheritsFromElement element
-- -- we need to adjust its position based on the previous element
-- previous = @data.child[#@data.child-1]
-- y, h = previous.y, previous.h
-- y += h + @data.padding
-- element.data.x = @data.x + @data.padding
-- element.data.y = y
-- element\setWidth @data.w - @data.padding*2
--
-- @window_area.remove = (element) =>
-- pop.elements.box.__parent.remove @, element
-- --TODO we need to adjust all elements' positions
remove: (element) =>
@window_area\remove element
return @
--NOTE was this even used?
--childAdded: (element) =>
-- table.insert @window_area.data, table.remove @data.child, @dataIndexOf element.data
-- table.insert @window_area, table.remove @child, @indexOf element
-- element\align!
-- print "worked?"
-- return @
2017-05-01 01:01:19 +00:00
maximize: =>
if @data.maximized
@setSize @data.previous.w, @data.previous.h
@align!
@move @data.previous.x - @data.x, @data.previous.y - @data.y
2017-05-01 01:01:19 +00:00
else
@data.previous.x = @data.x
@data.previous.y = @data.y
@data.previous.w = @data.w
@data.previous.h = @data.h
@data.x = @parent.data.x
@data.y = @parent.data.y
@setSize @parent.data.w, @parent.data.h
table.insert @parent.child, table.remove(@parent.child, @parent\indexOf @)
@align!
2017-05-01 01:01:19 +00:00
@data.maximized = not @data.maximized
return @
minimize: =>
@data.draw = false
return @
close: =>
@delete!