Adding UI for city editing
2
conf.lua
|
@ -12,7 +12,7 @@ function love.conf(t)
|
|||
|
||||
t.window.title = "dcEarth" -- The window title (string)
|
||||
t.window.icon = "icons/favicon.png" -- Filepath to an image to use as the window's icon (string)
|
||||
t.window.width = 1000 -- The window width (number)
|
||||
t.window.width = 1024 -- The window width (number)
|
||||
t.window.height = 640 -- The window height (number)
|
||||
t.window.borderless = false -- Remove all border visuals from the window (boolean)
|
||||
t.window.resizable = true -- Let the window be user-resizable (boolean)
|
||||
|
|
After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 341 B After Width: | Height: | Size: 357 B |
Before Width: | Height: | Size: 300 B After Width: | Height: | Size: 390 B |
Before Width: | Height: | Size: 301 B After Width: | Height: | Size: 381 B |
10
main.lua
|
@ -58,10 +58,7 @@ end
|
|||
function love.mousepressed( x, y, mouseButton, istouch, presses )
|
||||
local wx, wy = Camera.GetWorldCoordinate( x, y )
|
||||
|
||||
if button.selected and button.selected:contains( x, y ) then
|
||||
button.callback( button.selected )
|
||||
return button.selected:callback()
|
||||
end
|
||||
return button.mousepressed( x, y )
|
||||
end
|
||||
|
||||
function love.mousemoved( x, y, dx, dy, istouch )
|
||||
|
@ -89,9 +86,6 @@ function love.keypressed(key, code, isRepeat)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
do
|
||||
function love.textinput()
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -53,6 +53,48 @@ function city:formatDisplayInfo()
|
|||
CAPITAL: %s]]):format( self.name, self.country, self.x, self.y, self.pop, tostring(self.capital) )
|
||||
end
|
||||
|
||||
function city:delete()
|
||||
|
||||
end
|
||||
|
||||
function city:add()
|
||||
local n = #cities + 1
|
||||
cities[ n ] = self
|
||||
self.n = n
|
||||
|
||||
local idxPoints = #points + 1
|
||||
self.points = idxPoints
|
||||
points[ idxPoints ], points[ idxPoints + 1 ] = self.x, self.y
|
||||
|
||||
end
|
||||
|
||||
function city:moveTo(x, y)
|
||||
self.x, self.y = x, y
|
||||
if self.points then
|
||||
points[ self.points ] = x
|
||||
points[ self.points + 1 ] = y
|
||||
end
|
||||
if self.capital then
|
||||
caps[ self.caps ] = x
|
||||
caps[ self.caps + 1 ] = y
|
||||
end
|
||||
end
|
||||
|
||||
function city:toggleCapital()
|
||||
self.capital = not( self.capital )
|
||||
end
|
||||
|
||||
function t.newCity( tbl )
|
||||
return setmetatable({
|
||||
name = "",
|
||||
country = "",
|
||||
x = 0,
|
||||
y = 0,
|
||||
pop = 0,
|
||||
capital = false,
|
||||
}, citymt )
|
||||
end
|
||||
|
||||
function t.load( filename )
|
||||
|
||||
print( "=== LOADING CITIES. ===" )
|
||||
|
@ -72,7 +114,8 @@ function t.load( filename )
|
|||
local city = setmetatable({
|
||||
name = line:sub( 1, 39 ):gsub("%s+$",""),
|
||||
country = line:sub( 42, 82 ):gsub("%s+$",""),
|
||||
x = x, y = y, pop = pop, capital = capital
|
||||
x = x, y = y, pop = pop, capital = capital,
|
||||
n = n, points = idxPts, caps = capital and idxCaps
|
||||
}, citymt )
|
||||
cities[n] = city
|
||||
n = n + 1
|
||||
|
|
|
@ -4,12 +4,13 @@ local t = {
|
|||
name = "",
|
||||
tooltip = "",
|
||||
icon = false,
|
||||
lit = false,
|
||||
x = 8,
|
||||
y = 250,
|
||||
w = 176,
|
||||
w = 13 * 28 - 4,
|
||||
h = 24,
|
||||
group = "",
|
||||
visible = true,
|
||||
group = false,
|
||||
visible = false,
|
||||
callback = function( self ) return print( "clicked button: ", self.name, self.x, self.y, self.w, self.h, self.visible ) end
|
||||
}
|
||||
t.selected, t.next, t.prev = t, t, t
|
||||
|
@ -52,7 +53,7 @@ function t.draw( b )
|
|||
0,
|
||||
b.h / h )
|
||||
end
|
||||
if t.selected == b then
|
||||
if b.lit or t.selected == b then
|
||||
b:highlight()
|
||||
end
|
||||
end
|
||||
|
@ -92,15 +93,27 @@ function t.selectPrevInGroup()
|
|||
repeat t.selectPrev() until group == t.selected.group
|
||||
end
|
||||
|
||||
function t.displayGroup( group, show )
|
||||
--show/hide all buttons in a group
|
||||
--passing hide=true will hide the group, hide=false or nil will show the group
|
||||
--solo=true will hide all buttons outside the group
|
||||
function t.displayGroup( group, hide, solo )
|
||||
local b = t
|
||||
repeat
|
||||
b = b.next
|
||||
b.visible = ( b.group == group )
|
||||
local inGroup = (group == b.group)
|
||||
if solo or inGroup then
|
||||
b.visible = not(hide) and inGroup
|
||||
end
|
||||
until b == t
|
||||
t.visible = true
|
||||
end
|
||||
|
||||
function t.mousepressed( x, y )
|
||||
if t.selected and t.selected:contains( x, y ) then
|
||||
return t.selected:callback()
|
||||
end
|
||||
end
|
||||
|
||||
function t.deselect( b )
|
||||
t.selected = t
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ local loadLocation = false
|
|||
local folder = love.graphics.newImage( "icons/load.png" )
|
||||
folder:setFilter( "nearest", "nearest" )
|
||||
local loadButton = button.new{
|
||||
group = "loadModal",
|
||||
group = t,
|
||||
name = "load",
|
||||
callback = function()
|
||||
if not loadLocation then return end
|
||||
|
@ -25,7 +25,7 @@ local loadButton = button.new{
|
|||
local xIcon = love.graphics.newImage( "icons/x.png" )
|
||||
xIcon:setFilter( "nearest", "nearest" )
|
||||
local cancelButton = button.new{
|
||||
group = "loadModal",
|
||||
group = t,
|
||||
name = "cancel load",
|
||||
visible = false,
|
||||
icon = xIcon,
|
||||
|
@ -41,7 +41,7 @@ function t.start()
|
|||
loadLocation = loadLocation or map.path
|
||||
button.selected = loadButton
|
||||
loadButton.name = "save to "..loadLocation
|
||||
button.displayGroup( "loadModal", true )
|
||||
button.displayGroup( t, true )
|
||||
end
|
||||
|
||||
function t.draw()
|
||||
|
|
|
@ -1 +1,231 @@
|
|||
|
||||
local love = assert( love )
|
||||
local lg = assert( love ).graphics
|
||||
local utf8 = require 'utf8'
|
||||
|
||||
local button = require 'ui.button'
|
||||
local textinput = require 'ui.textinput'
|
||||
local modal = require 'ui.modal'
|
||||
local map = require 'map.map'
|
||||
local camera = require 'ui.camera'
|
||||
|
||||
local t = {}
|
||||
local city
|
||||
|
||||
|
||||
local function keypressed( key, code, isRepeat )
|
||||
if code == 'escape' then return modal.current:stop() end
|
||||
if modal.previous then return modal.previous.keypressed( key, code, isRepeat ) end
|
||||
end
|
||||
|
||||
--select modal: as normal, but clicking a city will proceed to the previously selected mode,
|
||||
--and clicking escape will clear the previously selected mode
|
||||
local selectModal = modal.new{}
|
||||
local textModal = modal.new{}
|
||||
local numberModal = modal.new{ cursor = 1 }
|
||||
local editModal = modal.new{ mousepressed = button.mousepressed, keypressed = keypressed, }
|
||||
local moveModal = modal.new{ mousepressed = button.mousepressed, keypressed = keypressed, mousemoved = button.selectIn }
|
||||
local deleteModal = modal.new{ mousepressed = button.mousepressed, keypressed = keypressed }
|
||||
local currentMode
|
||||
|
||||
button.new{ name = "NEW CITY",
|
||||
group = t,
|
||||
icon = lg.newImage("icons/layer-cities.png"),
|
||||
x = 615,
|
||||
y = 0,
|
||||
callback = function()
|
||||
city = map.cities.newCity()
|
||||
return editModal:start()
|
||||
end
|
||||
}
|
||||
|
||||
moveModal.button = button.new{ name = "MOVE CITY",
|
||||
group = t,
|
||||
x = 615,
|
||||
y = 28,
|
||||
callback = function( self )
|
||||
self.lit = true
|
||||
selectModal.mode = moveModal
|
||||
return selectModal:start()
|
||||
end,
|
||||
}
|
||||
|
||||
editModal.button = button.new{ name = "EDIT CITY",
|
||||
group = t,
|
||||
x = 615,
|
||||
y = 28 * 2,
|
||||
callback = function( self )
|
||||
self.lit = true
|
||||
selectModal.mode = editModal
|
||||
return selectModal:start()
|
||||
end,
|
||||
}
|
||||
|
||||
deleteModal.button = button.new{ name = "DELETE CITY",
|
||||
group = t,
|
||||
x = 615,
|
||||
y = 28 * 3,
|
||||
icon = lg.newImage("icons/x.png"),
|
||||
callback = function( self )
|
||||
self.lit = true
|
||||
selectModal.mode = deleteModal
|
||||
return selectModal:start()
|
||||
end,
|
||||
}
|
||||
|
||||
--editButtons
|
||||
local function editText( self )
|
||||
print( "editing: ", self.field, city.name )
|
||||
return textModal:start( self.field )
|
||||
end
|
||||
|
||||
local function editNumber( self )
|
||||
local field = self.field
|
||||
end
|
||||
|
||||
local editButtons = {
|
||||
|
||||
save = button.new{
|
||||
icon = lg.newImage( "icons/save.png" ),
|
||||
callback = function() print( "stop editing city" ) return editModal:stop() end,},
|
||||
|
||||
|
||||
name = button.new{ callback = editText },
|
||||
country = button.new{ callback = editText },
|
||||
x = button.new{ callback = editNumber },
|
||||
y = button.new{ callback = editNumber },
|
||||
capital = button.new{ callback = function() if city then return city:toggleCapital() end end },
|
||||
}
|
||||
|
||||
do
|
||||
local i = 0
|
||||
for key, b in pairs( editButtons ) do
|
||||
b.field = key
|
||||
b.name = key
|
||||
b.group = editModal
|
||||
b.x = 0
|
||||
b.y = 28 * i
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function editModal:start()
|
||||
modal.start( self )
|
||||
button.displayGroup( self, false, true )
|
||||
for k, b in pairs( editButtons ) do
|
||||
b.name = city[k] or b.name
|
||||
end
|
||||
end
|
||||
|
||||
function editModal.draw()
|
||||
return button:draw()
|
||||
end
|
||||
|
||||
function moveModal.update( dt )
|
||||
local x, y = love.mouse.getPosition()
|
||||
if y > 200 and love.mouse.isDown( 1 ) then
|
||||
local wx, wy = camera.GetWorldCoordinate( x, y )
|
||||
city:moveTo( wx, wy )
|
||||
end
|
||||
end
|
||||
|
||||
function moveModal.mousemoved( x, y, dx, dy, istouch )
|
||||
return button.selectIn( x, y )
|
||||
end
|
||||
|
||||
function moveModal.mousepressed( x, y, mouseButton, istouch, presses )
|
||||
if y < 200 then
|
||||
moveModal:stop()
|
||||
return button.mousepressed( x, y, mouseButton, istouch, presses )
|
||||
end
|
||||
if map.selected and mouseButton == 2 then
|
||||
city = map.selected
|
||||
end
|
||||
end
|
||||
|
||||
function numberModal:start( field )
|
||||
self.field = field
|
||||
return modal.start( self )
|
||||
end
|
||||
|
||||
function numberModal.keypressed( key, code, isrepeat )
|
||||
if code == 'backspace' then end
|
||||
if code == 'escape' then return numberModal:stop() end
|
||||
end
|
||||
|
||||
function numberModal.textinput( char )
|
||||
local str = tostring( city[ numberModal.field ] )
|
||||
local plus = str..char
|
||||
if tonumber( plus ) then
|
||||
city[ numberModal.field ] = plus
|
||||
editButtons[ numberModal.field ].name = plus
|
||||
end
|
||||
end
|
||||
|
||||
function textModal.textinput( char )
|
||||
print( "text input: ", char )
|
||||
city[textModal.field] = city[textModal.field] .. char
|
||||
editButtons[textModal.field].name = city[textModal.field]
|
||||
end
|
||||
|
||||
function textModal.mousepressed()
|
||||
end
|
||||
|
||||
function textModal:stop()
|
||||
self.field = nil
|
||||
return modal.stop( self )
|
||||
end
|
||||
|
||||
function textModal:start( field )
|
||||
self.field = field
|
||||
return modal.start( self )
|
||||
end
|
||||
|
||||
|
||||
function textModal.keypressed( key, code, isRepeat )
|
||||
if code == "backspace" then
|
||||
local text = city[textModal.field]
|
||||
-- get the byte offset to the last UTF-8 character in the string.
|
||||
local byteoffset = utf8.offset(text, -1)
|
||||
print( "textmodal: backspace", byteoffset )
|
||||
|
||||
if byteoffset then
|
||||
-- remove the last UTF-8 character.
|
||||
-- string.sub operates on bytes rather than UTF-8 characters, so we couldn't do string.sub(text, 1, -2).
|
||||
city[textModal.field] = text:sub( 1, byteoffset - 1)
|
||||
editButtons[textModal.field].name = city[textModal.field]
|
||||
end
|
||||
end
|
||||
if code == "escape" then
|
||||
return textModal:stop()
|
||||
end
|
||||
end
|
||||
|
||||
function selectModal.keypressed( key, code, isRepeat )
|
||||
if code == 'escape' then return selectModal:stop() end
|
||||
if modal.previous then return modal.previous.keypressed( key, code, isRepeat ) end
|
||||
end
|
||||
|
||||
function selectModal:stop()
|
||||
local mode = selectModal.mode
|
||||
if mode then
|
||||
mode.button.lit = false
|
||||
end
|
||||
return modal.stop( self )
|
||||
end
|
||||
|
||||
function selectModal.mousepressed( x, y, mouseButton, istouch, presses )
|
||||
if y < 200 then
|
||||
selectModal:stop()
|
||||
return button.mousepressed( x, y, mouseButton, istouch, presses )
|
||||
end
|
||||
|
||||
if map.selected then
|
||||
city = map.selected
|
||||
return selectModal.mode:start()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
return t
|
|
@ -5,6 +5,7 @@ local savemodal = require 'ui.savemodal'
|
|||
local loadmodal = require 'ui.loadmodal'
|
||||
local Camera = require 'ui.camera'
|
||||
local map = require 'map.map'
|
||||
local t = {}
|
||||
|
||||
local loadImg = love.graphics.newImage
|
||||
local layers = {
|
||||
|
@ -25,20 +26,23 @@ local layers = {
|
|||
|
||||
button.new{
|
||||
name = "LOAD", x = 250, y = 0,
|
||||
w = 28 * 13,
|
||||
group = t,
|
||||
callback = loadmodal.start,
|
||||
icon = love.graphics.newImage( "icons/load.png" )}
|
||||
button.new{
|
||||
name = "SAVE", x = 250, y = 28,
|
||||
w = 28 * 13,
|
||||
group = t,
|
||||
callback = savemodal.start,
|
||||
icon = love.graphics.newImage( "icons/save.png" )}
|
||||
button.new{
|
||||
name = "UNDO", x = 250, y = 2 * 28,
|
||||
w = 28 * 13,
|
||||
group = t,
|
||||
callback = map.undo,
|
||||
icon = love.graphics.newImage( "icons/undo.bmp" ) }
|
||||
|
||||
|
||||
local editButtons = {}
|
||||
local layerButtons = {}
|
||||
local showButtons = {}
|
||||
local visibilityIcon = love.graphics.newImage( "icons/eye.bmp" )
|
||||
local function updateVisibilityIcons()
|
||||
|
@ -57,9 +61,17 @@ end
|
|||
|
||||
local activeLayerButton
|
||||
local function back( self )
|
||||
self.visible = false
|
||||
map.setEditLayer()
|
||||
activeLayerButton.lit = false
|
||||
activeLayerButton = nil
|
||||
map.setEditLayer()
|
||||
button.displayGroup( t, false, true )
|
||||
for i, b in ipairs( editButtons ) do
|
||||
b.visible = true
|
||||
end
|
||||
for i, b in ipairs( showButtons ) do
|
||||
b.visible = true
|
||||
end
|
||||
self.visible = false
|
||||
return updateVisibilityIcons()
|
||||
end
|
||||
|
||||
|
@ -68,13 +80,18 @@ local backButton = button.new{
|
|||
visible = false,
|
||||
y = 5 * 28,
|
||||
x = 250,
|
||||
group = false,
|
||||
icon = love.graphics.newImage( "icons/up.bmp" ),
|
||||
callback = back,
|
||||
}
|
||||
|
||||
local function editLayer( self )
|
||||
self.lit = true
|
||||
map.setEditLayer( self.layer )
|
||||
activeLayerButton = self
|
||||
if self.menu then
|
||||
button.displayGroup( self.menu )
|
||||
end
|
||||
backButton.visible = true
|
||||
return updateVisibilityIcons()
|
||||
end
|
||||
|
@ -86,11 +103,7 @@ local function copy( i, target )
|
|||
return target
|
||||
end
|
||||
|
||||
|
||||
local layerButtons = {}
|
||||
local x = 250
|
||||
local soloButtons = {}
|
||||
local editButtons = {}
|
||||
for i = 1, #layers do
|
||||
|
||||
editButtons[i] = button.new( copy( i, {
|
||||
|
@ -98,7 +111,7 @@ for i = 1, #layers do
|
|||
x = x + (button.h + 4) * ( i - 1 ),
|
||||
w = 24,
|
||||
callback = editLayer,
|
||||
group = "edit",
|
||||
group = editButtons,
|
||||
tooltip = "edit "..layers[i].layer
|
||||
}))
|
||||
layerButtons[ 2 * i - 2 ] = editButtons[i]
|
||||
|
@ -110,14 +123,13 @@ for i = 1, #layers do
|
|||
name = "",
|
||||
callback = toggleVisibleLayer,
|
||||
icon = visibilityIcon,
|
||||
group = "show",
|
||||
group = showButtons,
|
||||
tooltip = "show "..layers[i].layer
|
||||
}))
|
||||
layerButtons[ 2 * i - 1 ] = showButtons[i]
|
||||
|
||||
end
|
||||
|
||||
local t = {}
|
||||
|
||||
function t.draw()
|
||||
--Status bar.
|
||||
|
@ -144,16 +156,22 @@ function t.draw()
|
|||
if map.selected then love.graphics.print( map.selected:formatDisplayInfo(), 0, 80 ) end
|
||||
if map.selectionLocked then end
|
||||
|
||||
love.graphics.setScissor( 0, 0, love.graphics.getWidth(), 200 )
|
||||
love.graphics.setScissor( 250, 0, love.graphics.getWidth() - 250, 200 )
|
||||
love.graphics.rectangle( "line", 0, 0 , 250, 200 )
|
||||
love.graphics.rectangle( "line", 250, 0, love.graphics.getWidth() - 250, 200 )
|
||||
love.graphics.rectangle( "line", 250, 0, button.w, 200 )
|
||||
|
||||
|
||||
love.graphics.setColor( 1, 1, 1, 0.8 )
|
||||
button:draw()
|
||||
|
||||
love.graphics.setColor( 1, 0, 0, 0.4 )
|
||||
if activeLayerButton then activeLayerButton:highlight() end
|
||||
end
|
||||
|
||||
do --button visibility
|
||||
button.displayGroup( t, false, true )
|
||||
button.displayGroup( editButtons, false, false )
|
||||
button.displayGroup( layerButtons, false, false )
|
||||
button.displayGroup( showButtons, false, false )
|
||||
end
|
||||
|
||||
return t
|
16
ui/modal.lua
|
@ -5,14 +5,17 @@ t.__index = t
|
|||
|
||||
local i = 0
|
||||
function t.start( self )
|
||||
print( "starting modal:", i + 1)
|
||||
love.graphics.setScissor(
|
||||
self.x or 0,
|
||||
self.y or 0,
|
||||
self.w or love.graphics.getWidth(),
|
||||
self.h or love.graphics.getDimensions())
|
||||
self.h or love.graphics.getHeight())
|
||||
|
||||
i = i + 1
|
||||
t[i] = t[i] or {}
|
||||
t[i] = { modal = self }
|
||||
t.previous = t[i]
|
||||
t.current = self
|
||||
|
||||
--store callbacks
|
||||
for name in pairs( self ) do
|
||||
|
@ -31,10 +34,11 @@ function t.start( self )
|
|||
end
|
||||
|
||||
function t.stop( self )
|
||||
print( "stopping modal:", i )
|
||||
--restore callbacks
|
||||
for name in pairs( self ) do
|
||||
if love[name] then
|
||||
love[name] = t[i][name]
|
||||
love[name] = t[i][name] or love[name]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -43,12 +47,16 @@ function t.stop( self )
|
|||
button.deselect()
|
||||
repeat
|
||||
b = b.next
|
||||
b.visible = t[i][b] or false --accessing a button's nil field is an error, so make sure that b.visible is a boolean
|
||||
b.visible = t[i][b] or false
|
||||
until b == button
|
||||
|
||||
|
||||
t.current = t[i].modal
|
||||
t[i] = nil
|
||||
i = i - 1
|
||||
t.previous = t[i - 1]
|
||||
|
||||
love.graphics.setScissor(0, 0, love.graphics.getDimensions())
|
||||
end
|
||||
|
||||
function t.new( modal )
|
||||
|
|
|
@ -8,7 +8,7 @@ local saveLocation = false
|
|||
local floppy = love.graphics.newImage( "icons/save.png" )
|
||||
floppy:setFilter( "nearest", "nearest" )
|
||||
local saveButton = button.new{
|
||||
group = "saveModal",
|
||||
group = t,
|
||||
name = "save",
|
||||
callback = function() map.save(); return t:stop() end,
|
||||
visible = false,
|
||||
|
@ -22,7 +22,7 @@ local saveButton = button.new{
|
|||
local xIcon = love.graphics.newImage( "icons/x.png" )
|
||||
xIcon:setFilter( "nearest", "nearest" )
|
||||
local cancelButton = button.new{
|
||||
group = "saveModal",
|
||||
group = t,
|
||||
name = "cancel",
|
||||
visible = false,
|
||||
icon = xIcon,
|
||||
|
@ -38,7 +38,7 @@ function t.start()
|
|||
saveLocation = saveLocation or map.path
|
||||
button.selected = saveButton
|
||||
saveButton.name = "save to "..saveLocation
|
||||
button.displayGroup( "saveModal", true )
|
||||
button.displayGroup( t, false, true )
|
||||
end
|
||||
|
||||
function t.draw()
|
||||
|
|
|
@ -54,7 +54,7 @@ function t.keypressed(key, code, isRepeat)
|
|||
if byteoffset then
|
||||
-- remove the last UTF-8 character.
|
||||
-- string.sub operates on bytes rather than UTF-8 characters, so we couldn't do string.sub(text, 1, -2).
|
||||
text = string.sub(text, 1, byteoffset - 1)
|
||||
t.currentModal.currentField = text:sub( 1, byteoffset - 1)
|
||||
end
|
||||
end
|
||||
if code == "escape" then
|
||||
|
|