Files
Techmino/parts/scenes/dict.lua
C6H12O6 + NaCl + H2O 9d3fb33d10 Improve Zictionary scene so we don't need to hyper tapping much than before (#926)
* 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
2023-07-31 03:20:24 +08:00

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