Files
Techmino/parts/scenes/dict.lua
2023-08-03 10:11:27 +08:00

438 lines
15 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 lastScrollPos
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') end
justSearched=true
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.dpadU,CHAR.controller.dpadD,CHAR.controller.dpadL,CHAR.controller.dpadR,
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,{
"OUCH! I can't seem to find any translated Help text anywhere.",
"\nI guess you'll have to switch to English and try again to read it instead!",
"\n\nOn another note, you could make an issue on GitHub or send this to Techmino's Discord server.",
"\nThe cause? I'm not sure... My guess is that there's something seriously wrong with the language files or the source code of this scene. BUT all the language files have a callback to English, and the original language - Chinese - has a version of the Help text! I'm not entirely certain if it worked or not, though.",
"\n\nOh, and it would be nice if you could let us know about it or you can fix it by yourself!",
"\n\n-- Sea, the one who rewrote the Zictionary scene and left this message just in case."
} 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(lastScrollPos-listBox.scrollPos)>26 then
lastScrollPos=listBox.scrollPos
listBox.selected=lastSelected
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
listBox.scrollPos=0
lastScrollPos=0
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)
lastScrollPos=listBox.scrollPos
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())
_updateInfoBox()
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=30,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