* Attempt to rework Zictionary scene * Rearrange the layout and handling most of thing * Remove ALPHA key, done keyboard handling, changed some key's behavior * Add Help text * Update Help text & fix some bugs * Update help text * Edit help text * Support gamepad * small changes in help text * small changes * Fix copy bug * small change * Add hint text to open Hint * Fix a bug that item accidentally selected when scrolling * Clean up a dumb function (Forget to cleanup in previous commit) * add backup solution, just in case * fix another bug * fix touch bug * Refix the scroll bug when touch * small code changes * small change in English file
428 lines
14 KiB
Lua
428 lines
14 KiB
Lua
local gc=love.graphics
|
|
|
|
local int,abs=math.floor,math.abs
|
|
local min,sin=math.min,math.sin
|
|
local ins=table.insert
|
|
local find=string.find
|
|
|
|
local scene={}
|
|
|
|
local dict-- Dict list
|
|
local result-- Result Lable
|
|
|
|
local lastTickInput
|
|
local searchWait-- Searching animation timer
|
|
|
|
local lastSearch-- Last searched string
|
|
local lastSelected -- Last selected item
|
|
local justSearched -- Just searched or not?
|
|
|
|
local currentFontSize=25 -- Current font size, default: 25
|
|
local showingHelp=false -- Help is triggered or not
|
|
local zoomWait=0 -- The last time zoom is triggered
|
|
|
|
local oldScrollPos=0
|
|
local lastMouseX,lastMouseY,lastTouchX,lastTouchY
|
|
|
|
local typeColor={
|
|
help=COLOR.Y,
|
|
org=COLOR.lF,
|
|
game=COLOR.lC,
|
|
term=COLOR.lR,
|
|
setup=COLOR.lY,
|
|
pattern=COLOR.lJ,
|
|
command=COLOR.lN,
|
|
english=COLOR.B,
|
|
name=COLOR.lV,
|
|
}
|
|
|
|
-- Scan the dictionary and return the list
|
|
local function _filter(word_org)
|
|
local word=word_org
|
|
word=word:gsub("[Tt][Ee][Tt][Rr][Ii][Ss]",CHAR.zChan.thinking)
|
|
if FNNS then word=word:gsub("[Pp]\97\116\114\101\111\110",CHAR.zChan.qualified) end
|
|
return word,word_org
|
|
end
|
|
local function _scanDict(D)
|
|
if not D[1][1] then return end
|
|
local cut=TABLE.cut
|
|
for i=1,#D do
|
|
local O=D[i]
|
|
O.title,O.title_Org=_filter(O[1])
|
|
O.keywords=O[2]
|
|
O.type=O[3]
|
|
O.content,O.content_Org=_filter(O[4])
|
|
O.url=O[5]
|
|
cut(O)
|
|
end
|
|
end
|
|
local function _getList() return result[1] and result or dict end
|
|
|
|
-- Drawing objects
|
|
local infoBox =WIDGET.newTextBox {name='infoBox',x=320,y=180,w=862,h=526,font=25,fix=true}
|
|
local inputBox=WIDGET.newInputBox{name='input', x=20, y=110,w=762,h=60, font=40,limit=32}
|
|
local listBox =WIDGET.newListBox {name='listBox',x=20, y=180,w=280,h=526,font=30,lineH=35,drawF=function(item,id,ifSel)
|
|
-- Draw list box
|
|
-- Background
|
|
if ifSel then
|
|
gc.setColor(1,1,1,.4)
|
|
gc.rectangle('fill',0,0,280,35)
|
|
end
|
|
|
|
-- Name & color
|
|
local item=_getList()[id]
|
|
GC.shadedPrint(item.title,10,-3,'left',1,COLOR.D,typeColor[item.type])
|
|
-- Draw list box /
|
|
end}
|
|
-- Drawing object /
|
|
|
|
|
|
|
|
-- Necessary local functions
|
|
-- Clear the result
|
|
local function _clearResult()
|
|
TABLE.cut(result)
|
|
listBox.selected=1
|
|
justSearched=true
|
|
searchWait,lastSearch=0,false
|
|
scene.widgetList.copy.hide=false
|
|
end
|
|
-- Search through the dictionary
|
|
local function _search()
|
|
local input=inputBox:getText():lower()
|
|
_clearResult()
|
|
local first
|
|
for i=1,#dict do
|
|
local pos=find(dict[i].title:lower(),input,nil,true) or find(dict[i].keywords:lower(),input,nil,true)
|
|
if pos==1 and not first then
|
|
ins(result,1,dict[i])
|
|
first=true
|
|
elseif pos then
|
|
ins(result,dict[i])
|
|
end
|
|
end
|
|
if #result>0 then
|
|
SFX.play('reach')
|
|
justSearched=true
|
|
end
|
|
lastSearch=input
|
|
end
|
|
|
|
-- Jump over n items
|
|
local function _jumpover(key,n)
|
|
n=n or 1
|
|
if key=='left' or key=='pageup' then
|
|
for _=1,n do scene.widgetList.listBox:arrowKey('up') end
|
|
else for _=1,n do scene.widgetList.listBox:arrowKey('down') end end
|
|
end
|
|
|
|
-- Copy the content
|
|
local function _copy()
|
|
local t=_getList()[listBox.selected]
|
|
t=t.title_Org..":\n"..t.content_Org..(t.url and "\n[ "..t.url.." ]\n" or "\n")..text.dictNote
|
|
love.system.setClipboardText(t)
|
|
scene.widgetList.copy.hide=true
|
|
MES.new('info',text.copyDone)
|
|
end
|
|
|
|
-- Update the infobox
|
|
local function _updateInfoBox(c)
|
|
local _t,t
|
|
if c==nil then
|
|
if showingHelp then
|
|
if text.dict.helpText then
|
|
t,_t=text.dict.helpText:repD(
|
|
CHAR.key.up, CHAR.key.down, CHAR.key.left, CHAR.key.right,
|
|
CHAR.controller.xboxX, CHAR.controller.xboxY, CHAR.controller.xboxA, CHAR.controller.xboxB,
|
|
|
|
CHAR.icon.help, CHAR.icon.copy, CHAR.icon.globe,
|
|
CHAR.icon.toUp, CHAR.icon.toDown, CHAR.key.winMenu
|
|
),true
|
|
else _t,t=true,{"NO HELP TEXT AVAILABLE!\n","Please switch language to English and try again to read the help text"} end
|
|
else _t,t=pcall(function() return _getList()[listBox.selected].content end) end
|
|
if _t then c=t else c={""} end
|
|
_t,t=nil,nil
|
|
end
|
|
local _w,c=FONT.get(currentFontSize):getWrap(c,840)
|
|
infoBox:setTexts(c)
|
|
end
|
|
|
|
-- Show Help or not
|
|
local function _toggleHelp()
|
|
local listBox = scene.widgetList.listBox
|
|
showingHelp = not showingHelp
|
|
if not showingHelp then
|
|
listBox.selected=lastSelected
|
|
scene.widgetList.copy.hide=false
|
|
end
|
|
_updateInfoBox()
|
|
end
|
|
|
|
-- Zoom and reset zoom
|
|
local function _openZoom() zoomWait=2 end
|
|
local function _resetZoom()
|
|
currentFontSize,infoBox.font=25,25
|
|
infoBox.lineH,infoBox.capacity=35,math.ceil((infoBox.h-10)/35)
|
|
_updateInfoBox()
|
|
MES.new("check",text.dict.sizeReset,1.26)
|
|
end
|
|
local function _setZoom(z)
|
|
if z~=0 then
|
|
currentFontSize=MATH.clamp(currentFontSize+z,15,40)
|
|
infoBox.font=currentFontSize
|
|
infoBox.lineH=currentFontSize*7/5 -- Recalculate the line's height
|
|
infoBox.capacity=math.ceil((infoBox.h-10)/infoBox.lineH)
|
|
_updateInfoBox()
|
|
_openZoom()
|
|
MES.new("check",text.dict.sizeChanged:repD(currentFontSize),1.26)
|
|
end
|
|
end
|
|
|
|
-- Checking if waiting countdown reach 0 to run the function.
|
|
--
|
|
-- currentCountdown: the variable that tracking waiting time
|
|
-- timeEndF: run this function if the time reach 0
|
|
-- nTimeEndF: run this function if the time has not reached 0
|
|
--
|
|
-- NOTE: This function will return the modified value of currentcountdown
|
|
local function _waitingfor(currentcountdown, timeEndF, nTimeEndF)
|
|
currentcountdown = currentcountdown or 0
|
|
timeEndF = timeEndF or function() end
|
|
nTimeEndF= nTimeEndF or function() end
|
|
|
|
if currentcountdown>0 then
|
|
currentcountdown=currentcountdown-love.timer.getDelta()
|
|
if currentcountdown<=0 then timeEndF() else nTimeEndF() end
|
|
end
|
|
return currentcountdown
|
|
end
|
|
|
|
local function fixScrollingByTouch(x,y,lastX,lastY)
|
|
if WIDGET.isFocus(listBox) then
|
|
if abs(oldScrollPos-listBox.scrollPos)>26 then
|
|
oldScrollPos=listBox.scrollPos
|
|
listBox.selected=lastSelected
|
|
listBox.scrollPos=oldScrollPos
|
|
else
|
|
lastSelected=listBox.selected
|
|
scene.widgetList.copy.hide=false
|
|
_updateInfoBox()
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Reset everything when opening Zictionary
|
|
function scene.enter()
|
|
dict=require("parts.language.dict_"..(SETTING.locale:find'zh' and 'zh' or SETTING.locale:find'ja' and 'ja' or SETTING.locale:find'vi' and 'vi' or 'en'))
|
|
_scanDict(dict)
|
|
|
|
inputBox:clear()
|
|
result={}
|
|
|
|
if showingHelp then _toggleHelp() end
|
|
|
|
searchWait=0
|
|
lastSelected=0
|
|
listBox.selected=1
|
|
lastSearch=false
|
|
|
|
if not MOBILE then WIDGET.focus(inputBox) end
|
|
BG.set('rainbow')
|
|
end
|
|
|
|
function scene.wheelMoved(_,y)
|
|
WHEELMOV(y)
|
|
end
|
|
function scene.keyDown(key)
|
|
-- Switching selected items
|
|
if key=='up' or key=='down' then
|
|
if not showingHelp then
|
|
if love.mouse.isDown(2,3) then
|
|
listBox:arrowKey(key)
|
|
return
|
|
elseif WIDGET.isFocus(listBox) then
|
|
listBox:scroll(key=='up' and -1 or 1)
|
|
return
|
|
end
|
|
end
|
|
infoBox:scroll(key=='up' and -3 or 3)
|
|
|
|
elseif (key=='left' or key=='pageup' or key=='right' or key=='pagedown')
|
|
then
|
|
if love.keyboard.isDown('lctrl','rctrl','lalt','ralt','lshift','rshift')
|
|
then _jumpover(key,12)
|
|
else _jumpover(key,1)
|
|
end
|
|
|
|
-- Copy & Zoom
|
|
elseif key=='cC' then _copy()
|
|
elseif love.keyboard.isDown('lctrl','rctrl') then
|
|
if key == 'c' and not showingHelp then _copy() return
|
|
elseif love.keyboard.isDown('-','=','kp-','kp+') then _setZoom((key=='-' or key=='kp-') and -5 or 5)
|
|
elseif love.keyboard.isDown('0','kp0') then _resetZoom() end
|
|
|
|
-- Clear search input, open URL
|
|
elseif key=='application' and not showingHelp then
|
|
local url=_getList()[listBox.selected].url
|
|
if url then love.system.openURL(url) end
|
|
elseif key=='delete' and not showingHelp then
|
|
if inputBox:hasText() then
|
|
_clearResult()
|
|
inputBox:clear()
|
|
SFX.play('hold')
|
|
end
|
|
|
|
-- Get out of Zictionary
|
|
elseif key=='escape' then
|
|
if inputBox:hasText() then scene.keyDown('delete')
|
|
elseif showingHelp then _toggleHelp()
|
|
else SCN.back()
|
|
end
|
|
-- Calling Help
|
|
elseif key=='f1' then _toggleHelp()
|
|
-- Focus on the search box
|
|
else
|
|
if not WIDGET.isFocus(inputBox) then WIDGET.focus(inputBox) end
|
|
return true
|
|
end
|
|
end
|
|
|
|
function scene.gamepadDown(key)
|
|
local Joystick=love.joystick.getJoysticks()[love.joystick.getJoystickCount()]
|
|
|
|
-- Scrolling text & zooming
|
|
if (key=='dpup' or key=='dpdown') then
|
|
if Joystick:isGamepadDown('a')
|
|
then _setZoom(key=='dpup' and 5 or -5)
|
|
else infoBox:scroll(key=='dpup' and -3 or 3)
|
|
end
|
|
-- Switching selected items
|
|
elseif key=='dpleft' or key=='dpright' then
|
|
_jumpover(key:gsub('dp',''),Joystick:isGamepadDown('a') and 12 or 1)
|
|
-- Activate help
|
|
elseif key=='y' then _toggleHelp()
|
|
-- Exit
|
|
elseif key=='back' then SCN.back()
|
|
end
|
|
end
|
|
|
|
function scene.mouseDown(mx,my)
|
|
lastMouseX,lastMouseY=mx,my
|
|
end
|
|
function scene.touchDown(mx,my)
|
|
lastTouchX,lastTouchY=mx,my
|
|
end
|
|
|
|
-- Check if left mouse key is released
|
|
function scene.mouseUp(mx,my)
|
|
fixScrollingByTouch(mx,my,lastMouseX,lastMouseY)
|
|
end
|
|
function scene.touchUp(mx,my)
|
|
fixScrollingByTouch(mx,my,lastTouchX,lastTouchY)
|
|
end
|
|
|
|
function scene.update(dt)
|
|
-- It's time to search?
|
|
local input=inputBox:getText()
|
|
if input~=lastTickInput then
|
|
if #input==0 then
|
|
_clearResult()
|
|
else
|
|
searchWait=.8
|
|
end
|
|
lastTickInput=input
|
|
end
|
|
searchWait=_waitingfor(
|
|
searchWait,
|
|
function() if #input>0 and input~=lastSearch then _search() end end
|
|
)
|
|
-- It's time to swap zoom buttons?
|
|
zoomWait=_waitingfor(
|
|
zoomWait,
|
|
function()
|
|
scene.widgetList.openzoom .hide=false
|
|
scene.widgetList.resetzoom.hide=false
|
|
scene.widgetList.zoomin .hide=true
|
|
scene.widgetList.zoomout .hide=true
|
|
end,
|
|
function()
|
|
scene.widgetList.openzoom .hide=true
|
|
scene.widgetList.resetzoom.hide=true
|
|
scene.widgetList.zoomin .hide=false
|
|
scene.widgetList.zoomout .hide=false
|
|
end
|
|
)
|
|
end
|
|
|
|
local function listStencil()
|
|
GC.rectangle('fill',20,180,280,526)
|
|
end
|
|
function scene.draw()
|
|
-- Order: list, info, keys
|
|
-- Draw background
|
|
gc.setColor(COLOR.dX)
|
|
gc.rectangle('fill',1194,180,80,526,5) -- keys
|
|
-- Draw outline
|
|
gc.setLineWidth(2)
|
|
gc.setColor(COLOR.Z)
|
|
gc.rectangle('line',1194,180,80,526,5) -- keys
|
|
-- Draw key seperating outline
|
|
gc.rectangle('line',1194,260,80,1,0) -- A | B
|
|
gc.rectangle('line',1194,410,80,1,0) -- B | C
|
|
gc.rectangle('line',1194,560,80,1,0) -- C | D
|
|
|
|
local list=_getList()
|
|
setFont(30)
|
|
|
|
-- Showing Help?
|
|
if showingHelp then
|
|
listBox.selected=0
|
|
scene.widgetList.copy.hide,scene.widgetList.link.hide=true,true
|
|
-- If not then, check the selected item if it is changed or not?
|
|
-- If yes, update lastSelected then update the textbox!
|
|
elseif justSearched then
|
|
listBox:setList(_getList())
|
|
justSearched=false
|
|
elseif lastSelected~=listBox.selected and not love.mouse.isDown(1) then
|
|
_updateInfoBox()
|
|
lastSelected=listBox.selected
|
|
scene.widgetList.copy.hide=false
|
|
end
|
|
|
|
if searchWait>0 then
|
|
local r=TIME()*2
|
|
local R=int(r)%7+1
|
|
gc.setColor(1,1,1,1-abs(r%1*2-1))
|
|
gc.draw(TEXTURE.miniBlock[R],821,140,TIME()*10%6.2832,7,7,2*DSCP[R][0][2]+1,2*(#BLOCKS[R][0]-DSCP[R][0][1])-1)
|
|
end
|
|
end
|
|
|
|
scene.widgetList={
|
|
WIDGET.newText {name='book', x=20, y=15, font=70,align='L',fText=CHAR.icon.zBook},
|
|
WIDGET.newText {name='title', x=100, y=15, font=70,align='L'},
|
|
listBox,
|
|
inputBox,
|
|
infoBox,
|
|
WIDGET.newKey {name='link', x=1234,y=600,w=60,font=45,fText=CHAR.icon.globe, code=pressKey'application',hideF=function() return not ((not (showingHelp or listBox.selected==0)) and _getList()[listBox.selected].url) end},
|
|
WIDGET.newKey {name='copy', x=1234,y=670,w=60,font=40,fText=CHAR.icon.copy, code=pressKey'cC'},
|
|
|
|
WIDGET.newKey {name='openzoom', x=1234,y=300,w=60,font=25,fText="aA", code=function() _openZoom() end,hide=false},
|
|
WIDGET.newKey {name='resetzoom',x=1234,y=370,w=60,font=25,fText="100%", code=function() _resetZoom() end,hide=false},
|
|
WIDGET.newKey {name='zoomin', x=1234,y=300,w=60,font=40,fText="A", code=function() _setZoom(5) end,hide=true},
|
|
WIDGET.newKey {name='zoomout', x=1234,y=370,w=60,font=40,fText="a", code=function() _setZoom(-5) end,hide=true},
|
|
|
|
WIDGET.newKey {name='pageup', x=1234,y=450,w=60,font=40,fText=CHAR.icon.toUp, hideF=function() return love.mouse.isDown(2,3) or showingHelp end,code=function() _jumpover('left') end},
|
|
WIDGET.newKey {name='pagedown', x=1234,y=520,w=60,font=40,fText=CHAR.icon.toDown,hideF=function() return love.mouse.isDown(2,3) or showingHelp end,code=function() _jumpover('right') end},
|
|
|
|
WIDGET.newKey {name='pageup1', x=1234,y=450,w=60,font=40,fText=CHAR.key.up, hideF=function() return not love.mouse.isDown(2,3) or showingHelp end,color="A"},
|
|
WIDGET.newKey {name='pagedown1',x=1234,y=520,w=60,font=40,fText=CHAR.key.down, hideF=function() return not love.mouse.isDown(2,3) or showingHelp end,color="A"},
|
|
|
|
WIDGET.newKey {name='help0', x=1234,y=220,w=60,font=40,fText=CHAR.icon.help, code=pressKey'f1',hideF=function() return showingHelp end},
|
|
WIDGET.newKey {name='help1', x=1234,y=220,w=60,font=40,fText=CHAR.icon.help, code=pressKey'f1',hideF=function() return not showingHelp end,color='lF'},
|
|
|
|
WIDGET.newButton{name='back', x=1165,y=60, w=170,h=80,sound='back',font=60,fText=CHAR.icon.back,code=backScene},
|
|
WIDGET.newText {name='buttontip',x=1274,y=110,w=762,h=60,font=40,align='R',fText=CHAR.controller.xboxY.."/[F1]: "..CHAR.icon.help}
|
|
}
|
|
-- NOTE: The gap between Link-Copy, Page, Zoom, Help is 60*1.5-10=80 :) The gap between 2 buttons in one group is 60+10=70
|
|
return scene
|