Round 6: compact preview, Optimize tab, deselect, click improvements
- Remove Preview toggle button from main dialog (preview is primary) - Preview buttons compact: Setup / All|One / Fit on single row - Preview navigation: L-click=prev, R-click=next in single mode - Preview title: "AniPhallow: All" or "AniPhallow: <name>" - Rename GB tab to Optimize, config labels updated accordingly - Combined separator texts: Source || Frames, Source || Optimized - GB canvases taller (240px each) to match Animations tab - Optimized canvas: candidate-based click with offset/silhouette support - R-click deselects in both Occurrences and Optimized canvases - Frame strip: L-click on selected frame deselects it - Remove view height config options (didn't work) - Clean up stale IDs (lblFrames, lblGbOpt, tabPreview)
This commit is contained in:
parent
b9b54867f1
commit
91af5f5691
278
aniphallow.lua
278
aniphallow.lua
|
|
@ -23,7 +23,7 @@ local SOURCE_VIEWPORT_W = 300
|
||||||
local SOURCE_VIEWPORT_H = 400
|
local SOURCE_VIEWPORT_H = 400
|
||||||
|
|
||||||
local MAX_ANIMS = 20 -- max number of dynamic animations
|
local MAX_ANIMS = 20 -- max number of dynamic animations
|
||||||
local TABS = { "Animations", "GB" }
|
local TABS = { "Animations", "Optimize" }
|
||||||
local DEFAULT_GB_TILE_W = 8 -- Default Game Boy tile width
|
local DEFAULT_GB_TILE_W = 8 -- Default Game Boy tile width
|
||||||
local DEFAULT_GB_TILE_H = 8 -- Default Game Boy tile height
|
local DEFAULT_GB_TILE_H = 8 -- Default Game Boy tile height
|
||||||
local GB_COLS = 16 -- tiles per row in optimized image (128px = GB standard)
|
local GB_COLS = 16 -- tiles per row in optimized image (128px = GB standard)
|
||||||
|
|
@ -119,7 +119,7 @@ local SETUP_IDS = {
|
||||||
"sepAnims", "cmbAnimList", "btnNewAnim", "btnDelAnim", "btnRenameAnim", "btnCloneAnim",
|
"sepAnims", "cmbAnimList", "btnNewAnim", "btnDelAnim", "btnRenameAnim", "btnCloneAnim",
|
||||||
"sepSource", "canvasSource",
|
"sepSource", "canvasSource",
|
||||||
"canvasStrips",
|
"canvasStrips",
|
||||||
"lblFrames", "btnFlipX", "btnFlipY", "btnMoveLeft", "btnMoveRight", "btnRemoveFrame",
|
"btnFlipX", "btnFlipY", "btnMoveLeft", "btnMoveRight", "btnRemoveFrame",
|
||||||
"btnFrameUp", "btnFrameDown", "btnFrameLeft", "btnFrameRight", "btnFrameReset",
|
"btnFrameUp", "btnFrameDown", "btnFrameLeft", "btnFrameRight", "btnFrameReset",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,13 +129,13 @@ local GB_IDS = {
|
||||||
"gbAnalyzeMode",
|
"gbAnalyzeMode",
|
||||||
"gbSimilarThreshold", "btnFindSimilar",
|
"gbSimilarThreshold", "btnFindSimilar",
|
||||||
"sepGbSource", "canvasGbSource",
|
"sepGbSource", "canvasGbSource",
|
||||||
"canvasGbOpt", "lblGbOpt",
|
"canvasGbOpt",
|
||||||
"btnGbSaveLayer", "btnGbCopyClipboard", "btnGbSave",
|
"btnGbSaveLayer", "btnGbCopyClipboard", "btnGbSave",
|
||||||
}
|
}
|
||||||
|
|
||||||
-- GB canvas heights (taller to match Animations tab and prevent resize on tab switch)
|
-- GB canvas heights (taller to match Animations tab and prevent resize on tab switch)
|
||||||
local GB_SRC_H = 220
|
local GB_SRC_H = 240
|
||||||
local GB_OPT_H = 220
|
local GB_OPT_H = 240
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- Module-level window/timer variables
|
-- Module-level window/timer variables
|
||||||
|
|
@ -209,7 +209,9 @@ local function loadPrefs()
|
||||||
-- Migrate old tab names
|
-- Migrate old tab names
|
||||||
if v == "Setup" then v = "Animations"
|
if v == "Setup" then v = "Animations"
|
||||||
elseif v == "Render" then v = "Animations"
|
elseif v == "Render" then v = "Animations"
|
||||||
elseif v == "Preview" then v = "Animations" end
|
elseif v == "Preview" then v = "Animations"
|
||||||
|
elseif v == "GB" then v = "Optimize"
|
||||||
|
end
|
||||||
S.currentTab = v
|
S.currentTab = v
|
||||||
end
|
end
|
||||||
if k == "animNames" and v ~= "" then
|
if k == "animNames" and v ~= "" then
|
||||||
|
|
@ -967,6 +969,21 @@ local function buildOptimizedImage(img, tiles, compress, silhouette, silhouetteC
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
-- Helper: get preview title based on current mode/animation
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
local function getPreviewTitle()
|
||||||
|
if S.previewMode == "all" then
|
||||||
|
return "AniPhallow: All"
|
||||||
|
else
|
||||||
|
local name = ""
|
||||||
|
if S.previewSingleIdx >= 1 and S.previewSingleIdx <= #S.animNames then
|
||||||
|
name = S.animNames[S.previewSingleIdx]
|
||||||
|
end
|
||||||
|
return "AniPhallow: " .. name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
-- Preview Window (primary window)
|
-- Preview Window (primary window)
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
@ -1017,7 +1034,7 @@ openPreviewWindow = function()
|
||||||
pvHeight = math.max(pvHeight, 40)
|
pvHeight = math.max(pvHeight, 40)
|
||||||
|
|
||||||
previewDlg = Dialog{
|
previewDlg = Dialog{
|
||||||
title = "AniPhallow Preview",
|
title = getPreviewTitle(),
|
||||||
onclose = function()
|
onclose = function()
|
||||||
if previewTimer then pcall(function() previewTimer:stop() end) end
|
if previewTimer then pcall(function() previewTimer:stop() end) end
|
||||||
if pvRefreshTimer then pcall(function() pvRefreshTimer:stop() end) end
|
if pvRefreshTimer then pcall(function() pvRefreshTimer:stop() end) end
|
||||||
|
|
@ -1026,12 +1043,10 @@ openPreviewWindow = function()
|
||||||
previewDlg = nil
|
previewDlg = nil
|
||||||
S.previewWindowOpen = false
|
S.previewWindowOpen = false
|
||||||
savePrefs()
|
savePrefs()
|
||||||
if mainDlg then
|
|
||||||
pcall(function() mainDlg:modify{ id = "tabPreview", text = " Preview " } end)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- All buttons on the same row: Setup, toggle mode, Fit
|
||||||
previewDlg:button{
|
previewDlg:button{
|
||||||
id = "pvSetup",
|
id = "pvSetup",
|
||||||
text = "Setup",
|
text = "Setup",
|
||||||
|
|
@ -1042,7 +1057,7 @@ openPreviewWindow = function()
|
||||||
|
|
||||||
previewDlg:button{
|
previewDlg:button{
|
||||||
id = "pvToggleMode",
|
id = "pvToggleMode",
|
||||||
text = S.previewMode == "all" and "Show One" or "Show All",
|
text = S.previewMode == "all" and "All" or "One",
|
||||||
onclick = function()
|
onclick = function()
|
||||||
if S.previewMode == "all" then
|
if S.previewMode == "all" then
|
||||||
S.previewMode = "single"
|
S.previewMode = "single"
|
||||||
|
|
@ -1050,52 +1065,22 @@ openPreviewWindow = function()
|
||||||
if S.previewSingleIdx < 1 then S.previewSingleIdx = 1 end
|
if S.previewSingleIdx < 1 then S.previewSingleIdx = 1 end
|
||||||
if S.previewSingleIdx > #S.animNames then S.previewSingleIdx = #S.animNames end
|
if S.previewSingleIdx > #S.animNames then S.previewSingleIdx = #S.animNames end
|
||||||
end
|
end
|
||||||
previewDlg:modify{ id = "pvToggleMode", text = "Show All" }
|
|
||||||
previewDlg:modify{ id = "pvPrev", visible = true }
|
|
||||||
previewDlg:modify{ id = "pvNext", visible = true }
|
|
||||||
previewDlg:modify{ id = "pvAnimName", visible = true }
|
|
||||||
else
|
else
|
||||||
S.previewMode = "all"
|
S.previewMode = "all"
|
||||||
previewDlg:modify{ id = "pvToggleMode", text = "Show One" }
|
|
||||||
previewDlg:modify{ id = "pvPrev", visible = false }
|
|
||||||
previewDlg:modify{ id = "pvNext", visible = false }
|
|
||||||
previewDlg:modify{ id = "pvAnimName", visible = false }
|
|
||||||
end
|
end
|
||||||
previewDlg:repaint()
|
-- Close and reopen to update title
|
||||||
|
pcall(function() previewDlg:close() end)
|
||||||
|
openPreviewWindow()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Navigation buttons on a new row, only in single mode
|
|
||||||
previewDlg:newrow()
|
|
||||||
previewDlg:button{
|
previewDlg:button{
|
||||||
id = "pvPrev",
|
id = "pvFit",
|
||||||
text = "<-",
|
text = "Fit",
|
||||||
visible = (S.previewMode == "single"),
|
|
||||||
onclick = function()
|
onclick = function()
|
||||||
if #S.animNames > 0 then
|
-- Close and reopen to refit
|
||||||
S.previewSingleIdx = S.previewSingleIdx - 1
|
pcall(function() previewDlg:close() end)
|
||||||
if S.previewSingleIdx < 1 then S.previewSingleIdx = #S.animNames end
|
openPreviewWindow()
|
||||||
-- Update name label
|
|
||||||
pcall(function()
|
|
||||||
previewDlg:modify{ id = "pvAnimName", text = S.animNames[S.previewSingleIdx] or "" }
|
|
||||||
end)
|
|
||||||
previewDlg:repaint()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
previewDlg:button{
|
|
||||||
id = "pvNext",
|
|
||||||
text = "->",
|
|
||||||
visible = (S.previewMode == "single"),
|
|
||||||
onclick = function()
|
|
||||||
if #S.animNames > 0 then
|
|
||||||
S.previewSingleIdx = S.previewSingleIdx + 1
|
|
||||||
if S.previewSingleIdx > #S.animNames then S.previewSingleIdx = 1 end
|
|
||||||
pcall(function()
|
|
||||||
previewDlg:modify{ id = "pvAnimName", text = S.animNames[S.previewSingleIdx] or "" }
|
|
||||||
end)
|
|
||||||
previewDlg:repaint()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1171,6 +1156,28 @@ openPreviewWindow = function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
onmousedown = function(ev)
|
||||||
|
-- In single mode: L-click=prev anim, R-click=next anim
|
||||||
|
if S.previewMode == "single" then
|
||||||
|
if ev.button == MouseButton.LEFT then
|
||||||
|
if #S.animNames > 0 then
|
||||||
|
S.previewSingleIdx = S.previewSingleIdx - 1
|
||||||
|
if S.previewSingleIdx < 1 then S.previewSingleIdx = #S.animNames end
|
||||||
|
-- Close and reopen to update title
|
||||||
|
pcall(function() previewDlg:close() end)
|
||||||
|
openPreviewWindow()
|
||||||
|
end
|
||||||
|
elseif ev.button == MouseButton.RIGHT then
|
||||||
|
if #S.animNames > 0 then
|
||||||
|
S.previewSingleIdx = S.previewSingleIdx + 1
|
||||||
|
if S.previewSingleIdx > #S.animNames then S.previewSingleIdx = 1 end
|
||||||
|
-- Close and reopen to update title
|
||||||
|
pcall(function() previewDlg:close() end)
|
||||||
|
openPreviewWindow()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
onwheel = function(ev)
|
onwheel = function(ev)
|
||||||
local dz = ev.deltaY < 0 and 1 or -1
|
local dz = ev.deltaY < 0 and 1 or -1
|
||||||
if S.previewMode == "single" then
|
if S.previewMode == "single" then
|
||||||
|
|
@ -1182,29 +1189,6 @@ openPreviewWindow = function()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Animation name label (only in single mode)
|
|
||||||
local singleName = ""
|
|
||||||
if S.previewSingleIdx >= 1 and S.previewSingleIdx <= #S.animNames then
|
|
||||||
singleName = S.animNames[S.previewSingleIdx]
|
|
||||||
end
|
|
||||||
previewDlg:label{
|
|
||||||
id = "pvAnimName",
|
|
||||||
text = singleName,
|
|
||||||
visible = (S.previewMode == "single")
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Adjust button: re-fits window to content by closing and reopening
|
|
||||||
previewDlg:newrow()
|
|
||||||
previewDlg:button{
|
|
||||||
id = "pvAdjust",
|
|
||||||
text = "Adjust",
|
|
||||||
onclick = function()
|
|
||||||
-- Close and reopen to refit
|
|
||||||
pcall(function() previewDlg:close() end)
|
|
||||||
openPreviewWindow()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
previewTimer = Timer{
|
previewTimer = Timer{
|
||||||
interval = S.animSpeed / 1000.0,
|
interval = S.animSpeed / 1000.0,
|
||||||
ontick = function()
|
ontick = function()
|
||||||
|
|
@ -1267,18 +1251,18 @@ openMainDialog = function()
|
||||||
dlg:repaint()
|
dlg:repaint()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Switch visible tab (only Animations and GB)
|
-- Switch visible tab (only Animations and Optimize)
|
||||||
local function switchTab(tab)
|
local function switchTab(tab)
|
||||||
S.currentTab = tab
|
S.currentTab = tab
|
||||||
local isSetup = (tab == "Animations")
|
local isSetup = (tab == "Animations")
|
||||||
local isGB = (tab == "GB")
|
local isOpt = (tab == "Optimize")
|
||||||
for _, id in ipairs(SETUP_IDS) do
|
for _, id in ipairs(SETUP_IDS) do
|
||||||
pcall(function() dlg:modify{ id = id, visible = isSetup } end)
|
pcall(function() dlg:modify{ id = id, visible = isSetup } end)
|
||||||
end
|
end
|
||||||
for _, id in ipairs(GB_IDS) do
|
for _, id in ipairs(GB_IDS) do
|
||||||
pcall(function() dlg:modify{ id = id, visible = isGB } end)
|
pcall(function() dlg:modify{ id = id, visible = isOpt } end)
|
||||||
end
|
end
|
||||||
-- Update tab button labels for Animations and GB only
|
-- Update tab button labels
|
||||||
for _, t in ipairs(TABS) do
|
for _, t in ipairs(TABS) do
|
||||||
local label = (t == tab) and ("[" .. t .. "]") or (" " .. t .. " ")
|
local label = (t == tab) and ("[" .. t .. "]") or (" " .. t .. " ")
|
||||||
pcall(function() dlg:modify{ id = "tab" .. t, text = label } end)
|
pcall(function() dlg:modify{ id = "tab" .. t, text = label } end)
|
||||||
|
|
@ -1316,7 +1300,7 @@ openMainDialog = function()
|
||||||
mainDlg = dlg
|
mainDlg = dlg
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
-- TAB BUTTONS (Animations, GB, Preview toggle)
|
-- TAB BUTTONS (Animations, Optimize)
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
for _, tab in ipairs(TABS) do
|
for _, tab in ipairs(TABS) do
|
||||||
local label = (tab == S.currentTab) and ("[" .. tab .. "]") or (" " .. tab .. " ")
|
local label = (tab == S.currentTab) and ("[" .. tab .. "]") or (" " .. tab .. " ")
|
||||||
|
|
@ -1329,23 +1313,6 @@ openMainDialog = function()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Preview toggle button
|
|
||||||
dlg:button{
|
|
||||||
id = "tabPreview",
|
|
||||||
text = S.previewWindowOpen and "[Preview]" or " Preview ",
|
|
||||||
onclick = function()
|
|
||||||
if previewDlg then
|
|
||||||
-- Close it
|
|
||||||
pcall(function() previewDlg:close() end)
|
|
||||||
-- previewDlg onclose handler sets S.previewWindowOpen = false
|
|
||||||
dlg:modify{ id = "tabPreview", text = " Preview " }
|
|
||||||
else
|
|
||||||
openPreviewWindow()
|
|
||||||
dlg:modify{ id = "tabPreview", text = "[Preview]" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
-- Config button (opens sub-dialog) - visible on ALL tabs
|
-- Config button (opens sub-dialog) - visible on ALL tabs
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
|
|
@ -1359,9 +1326,9 @@ openMainDialog = function()
|
||||||
d:slider{ id = "animSpeed", label = "Speed (ms):", min = 50, max = 1000, value = S.animSpeed }
|
d:slider{ id = "animSpeed", label = "Speed (ms):", min = 50, max = 1000, value = S.animSpeed }
|
||||||
d:check{ id = "useBgColor", text = "Solid background", selected = S.useBgColor }
|
d:check{ id = "useBgColor", text = "Solid background", selected = S.useBgColor }
|
||||||
d:color{ id = "bgColor", label = "Bg Color:", color = S.bgColor }
|
d:color{ id = "bgColor", label = "Bg Color:", color = S.bgColor }
|
||||||
d:separator{ text = "GB Options" }
|
d:separator{ text = "Optimization" }
|
||||||
d:number{ id = "gbTileW", label = "GB Tile W:", text = tostring(S.gbTileW), decimals = 0 }
|
d:number{ id = "gbTileW", label = "Tile W:", text = tostring(S.gbTileW), decimals = 0 }
|
||||||
d:number{ id = "gbTileH", label = "GB Tile H:", text = tostring(S.gbTileH), decimals = 0 }
|
d:number{ id = "gbTileH", label = "Tile H:", text = tostring(S.gbTileH), decimals = 0 }
|
||||||
local silColor = S.gbSilhouetteColor
|
local silColor = S.gbSilhouetteColor
|
||||||
if not S.gbSilhouetteColorSet then
|
if not S.gbSilhouetteColorSet then
|
||||||
silColor = getDarkestPaletteColor()
|
silColor = getDarkestPaletteColor()
|
||||||
|
|
@ -1551,7 +1518,7 @@ openMainDialog = function()
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
-- Source canvas (L-click=add, R-click=add flipped)
|
-- Source canvas (L-click=add, R-click=add flipped)
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
dlg:separator{ id = "sepSource", text = "Source (L-click=add frame, R-click=add flipped)" }
|
dlg:separator{ id = "sepSource", text = "Source (L-click=add, R-click=flip add) || Frames (L-click=select, R-click=remove, drag=move)" }
|
||||||
|
|
||||||
dlg:canvas{
|
dlg:canvas{
|
||||||
id = "canvasSource",
|
id = "canvasSource",
|
||||||
|
|
@ -1777,6 +1744,12 @@ openMainDialog = function()
|
||||||
local idx = row * thumbsPerRow + col + 1
|
local idx = row * thumbsPerRow + col + 1
|
||||||
if idx >= 1 and idx <= #frames then
|
if idx >= 1 and idx <= #frames then
|
||||||
if ev.button == MouseButton.LEFT then
|
if ev.button == MouseButton.LEFT then
|
||||||
|
-- Click on already-selected frame: deselect
|
||||||
|
if S.selectedFrame == idx then
|
||||||
|
S.selectedFrame = 0
|
||||||
|
dlg:repaint()
|
||||||
|
return -- don't start drag
|
||||||
|
end
|
||||||
S.selectedFrame = idx
|
S.selectedFrame = idx
|
||||||
S.frameDragging = true
|
S.frameDragging = true
|
||||||
S.frameDragFrom = idx
|
S.frameDragFrom = idx
|
||||||
|
|
@ -1830,10 +1803,8 @@ openMainDialog = function()
|
||||||
}
|
}
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
-- Frame action buttons
|
-- Frame action buttons (no separator - combined into sepSource)
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
dlg:separator{ id = "lblFrames", text = "Frames (L-click=select, R-click=remove, drag=reorder)" }
|
|
||||||
|
|
||||||
dlg:button{
|
dlg:button{
|
||||||
id = "btnFlipX",
|
id = "btnFlipX",
|
||||||
text = "FlipX",
|
text = "FlipX",
|
||||||
|
|
@ -1999,9 +1970,9 @@ openMainDialog = function()
|
||||||
}
|
}
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
-- GB TAB
|
-- OPTIMIZE TAB
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
dlg:separator{ id = "sepGb", text = "GB Tile Optimizer", visible = false }
|
dlg:separator{ id = "sepGb", text = "Tile Optimizer", visible = false }
|
||||||
|
|
||||||
-- Row: Flip + Offset + Silhouette + Compress (reordered)
|
-- Row: Flip + Offset + Silhouette + Compress (reordered)
|
||||||
dlg:check{
|
dlg:check{
|
||||||
|
|
@ -2118,7 +2089,7 @@ openMainDialog = function()
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
-- Occurrences in Source canvas (BEFORE Optimized)
|
-- Occurrences in Source canvas (BEFORE Optimized)
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
dlg:separator{ id = "sepGbSource", text = "Occurrences in Source", visible = false }
|
dlg:separator{ id = "sepGbSource", text = "Source || Optimized (L-click=check occurrences, R-click=deselect)", visible = false }
|
||||||
|
|
||||||
local GB_SRC_W = SOURCE_VIEWPORT_W
|
local GB_SRC_W = SOURCE_VIEWPORT_W
|
||||||
|
|
||||||
|
|
@ -2202,6 +2173,12 @@ openMainDialog = function()
|
||||||
S.gbSrcDragging = true
|
S.gbSrcDragging = true
|
||||||
S.gbSrcDragLastX = ev.x
|
S.gbSrcDragLastX = ev.x
|
||||||
S.gbSrcDragLastY = ev.y
|
S.gbSrcDragLastY = ev.y
|
||||||
|
elseif ev.button == MouseButton.RIGHT then
|
||||||
|
-- R-click: deselect
|
||||||
|
if S.gbSelectedTile > 0 then
|
||||||
|
S.gbSelectedTile = 0
|
||||||
|
dlg:repaint()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
onmousemove = function(ev)
|
onmousemove = function(ev)
|
||||||
|
|
@ -2283,7 +2260,7 @@ openMainDialog = function()
|
||||||
}
|
}
|
||||||
|
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
-- Optimized Spritesheet canvas (L-click=select)
|
-- Optimized Spritesheet canvas (L-click=select, R-click=deselect)
|
||||||
----------------------------------------------------------------
|
----------------------------------------------------------------
|
||||||
local GB_OPT_W = SOURCE_VIEWPORT_W
|
local GB_OPT_W = SOURCE_VIEWPORT_W
|
||||||
|
|
||||||
|
|
@ -2453,6 +2430,12 @@ openMainDialog = function()
|
||||||
S.gbDragging = true
|
S.gbDragging = true
|
||||||
S.gbDragLastX = ev.x
|
S.gbDragLastX = ev.x
|
||||||
S.gbDragLastY = ev.y
|
S.gbDragLastY = ev.y
|
||||||
|
elseif ev.button == MouseButton.RIGHT then
|
||||||
|
-- R-click: deselect
|
||||||
|
if S.gbSelectedTile > 0 then
|
||||||
|
S.gbSelectedTile = 0
|
||||||
|
dlg:repaint()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
onmousemove = function(ev)
|
onmousemove = function(ev)
|
||||||
|
|
@ -2474,7 +2457,7 @@ openMainDialog = function()
|
||||||
end,
|
end,
|
||||||
onmouseup = function(ev)
|
onmouseup = function(ev)
|
||||||
if S.gbDragging and not S.gbOptIsDrag then
|
if S.gbDragging and not S.gbOptIsDrag then
|
||||||
-- Click: select tile
|
-- Click: select tile using candidate-based logic
|
||||||
if not S.gbOptImage then
|
if not S.gbOptImage then
|
||||||
S.gbDragging = false
|
S.gbDragging = false
|
||||||
return
|
return
|
||||||
|
|
@ -2485,33 +2468,86 @@ openMainDialog = function()
|
||||||
local pixelX = math.floor((S.gbOptClickX + S.gbScrollX) / z)
|
local pixelX = math.floor((S.gbOptClickX + S.gbScrollX) / z)
|
||||||
local pixelY = math.floor((S.gbOptClickY + S.gbScrollY) / z)
|
local pixelY = math.floor((S.gbOptClickY + S.gbScrollY) / z)
|
||||||
|
|
||||||
|
-- Find all tiles whose positions cover the clicked pixel
|
||||||
|
local candidates = {}
|
||||||
|
|
||||||
if S.gbCompress then
|
if S.gbCompress then
|
||||||
|
-- In compress mode, tiles are laid out in a grid
|
||||||
local col = math.floor(pixelX / tw)
|
local col = math.floor(pixelX / tw)
|
||||||
local row = math.floor(pixelY / th)
|
local row = math.floor(pixelY / th)
|
||||||
local idx = row * S.gbCols + col + 1
|
local idx = row * S.gbCols + col + 1
|
||||||
if idx >= 1 and idx <= #S.gbTiles then
|
if idx >= 1 and idx <= #S.gbTiles then
|
||||||
-- Toggle: deselect if same tile clicked again
|
local isOpaque = false
|
||||||
if S.gbSelectedTile == idx then
|
if pixelX >= 0 and pixelX < S.gbOptImage.width
|
||||||
S.gbSelectedTile = 0
|
and pixelY >= 0 and pixelY < S.gbOptImage.height then
|
||||||
else
|
isOpaque = pc.rgbaA(S.gbOptImage:getPixel(pixelX, pixelY)) > 0
|
||||||
S.gbSelectedTile = idx
|
|
||||||
end
|
end
|
||||||
|
table.insert(candidates, { tileIdx = idx, opaque = isOpaque })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Also check silhouette areas in non-compress mode is N/A here,
|
||||||
|
-- but in compress mode with silhouette there's only one tile per grid cell
|
||||||
else
|
else
|
||||||
local clickTileX = math.floor(pixelX / tw) * tw
|
-- Non-compress mode: candidate-based click like Occurrences canvas
|
||||||
local clickTileY = math.floor(pixelY / th) * th
|
|
||||||
for i, tile in ipairs(S.gbTiles) do
|
for i, tile in ipairs(S.gbTiles) do
|
||||||
local pos = tile.positions[1]
|
local pos = tile.positions[1]
|
||||||
if pos.x == clickTileX and pos.y == clickTileY then
|
if pixelX >= pos.x and pixelX < pos.x + tw and
|
||||||
-- Toggle: deselect if same tile clicked again
|
pixelY >= pos.y and pixelY < pos.y + th then
|
||||||
if S.gbSelectedTile == i then
|
local isOpaque = false
|
||||||
S.gbSelectedTile = 0
|
if S.gbOptImage and pixelX >= 0 and pixelX < S.gbOptImage.width
|
||||||
else
|
and pixelY >= 0 and pixelY < S.gbOptImage.height then
|
||||||
S.gbSelectedTile = i
|
isOpaque = pc.rgbaA(S.gbOptImage:getPixel(pixelX, pixelY)) > 0
|
||||||
end
|
end
|
||||||
|
table.insert(candidates, { tileIdx = i, opaque = isOpaque })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Also check silhouette positions (duplicate tile positions rendered as silhouettes)
|
||||||
|
if S.gbSilhouette then
|
||||||
|
for i, tile in ipairs(S.gbTiles) do
|
||||||
|
for pi = 2, #tile.positions do
|
||||||
|
local pos = tile.positions[pi]
|
||||||
|
if pixelX >= pos.x and pixelX < pos.x + tw and
|
||||||
|
pixelY >= pos.y and pixelY < pos.y + th then
|
||||||
|
local isOpaque = false
|
||||||
|
if S.gbOptImage and pixelX >= 0 and pixelX < S.gbOptImage.width
|
||||||
|
and pixelY >= 0 and pixelY < S.gbOptImage.height then
|
||||||
|
isOpaque = pc.rgbaA(S.gbOptImage:getPixel(pixelX, pixelY)) > 0
|
||||||
|
end
|
||||||
|
-- Check if not already in candidates
|
||||||
|
local alreadyIn = false
|
||||||
|
for _, c in ipairs(candidates) do
|
||||||
|
if c.tileIdx == i then alreadyIn = true; break end
|
||||||
|
end
|
||||||
|
if not alreadyIn then
|
||||||
|
table.insert(candidates, { tileIdx = i, opaque = isOpaque })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #candidates > 0 then
|
||||||
|
-- Sort: opaque tiles first
|
||||||
|
table.sort(candidates, function(a, b)
|
||||||
|
if a.opaque ~= b.opaque then return a.opaque end
|
||||||
|
return a.tileIdx < b.tileIdx
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Cycle through candidates if current selection is among them
|
||||||
|
local found = false
|
||||||
|
for ci, cand in ipairs(candidates) do
|
||||||
|
if cand.tileIdx == S.gbSelectedTile then
|
||||||
|
local nextIdx = (ci % #candidates) + 1
|
||||||
|
S.gbSelectedTile = candidates[nextIdx].tileIdx
|
||||||
|
found = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if not found then
|
||||||
|
S.gbSelectedTile = candidates[1].tileIdx
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
S.gbDragging = false
|
S.gbDragging = false
|
||||||
|
|
@ -2526,8 +2562,6 @@ openMainDialog = function()
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
dlg:separator{ id = "lblGbOpt", text = "Optimized Spritesheet (L-click=select)", visible = false }
|
|
||||||
|
|
||||||
-- Save as Layer button
|
-- Save as Layer button
|
||||||
dlg:button{
|
dlg:button{
|
||||||
id = "btnGbSaveLayer",
|
id = "btnGbSaveLayer",
|
||||||
|
|
@ -2678,7 +2712,7 @@ openMainDialog = function()
|
||||||
interval = 0.5,
|
interval = 0.5,
|
||||||
ontick = function()
|
ontick = function()
|
||||||
local isDragging = S.dragging or S.gbDragging or S.gbSrcDragging
|
local isDragging = S.dragging or S.gbDragging or S.gbSrcDragging
|
||||||
if S.currentTab == "GB" and not isDragging then
|
if S.currentTab == "Optimize" and not isDragging then
|
||||||
gbRefreshCounter = gbRefreshCounter + 1
|
gbRefreshCounter = gbRefreshCounter + 1
|
||||||
if gbRefreshCounter >= 4 then
|
if gbRefreshCounter >= 4 then
|
||||||
gbRefreshCounter = 0
|
gbRefreshCounter = 0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue