main_screen.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. # -*- coding: utf-8 -*-
  2. import pygame
  3. from pygame.locals import *
  4. from sys import exit
  5. import os
  6. import sys
  7. from operator import itemgetter
  8. from libs import easing
  9. from datetime import datetime
  10. from beeprint import pp
  11. ## local package import
  12. from constants import ICON_TYPES,icon_ext,icon_width,icon_height,RUNEVT
  13. from icon_item import IconItem
  14. from page import Page,PageStack
  15. from title_bar import TitleBar
  16. from foot_bar import FootBar
  17. from constants import Width,Height,bg_color
  18. from util_funcs import midRect,FileExists,ReplaceSuffix,ReadTheFileContent,CmdClean,MakeExecutable,SkinMap
  19. from keys_def import CurKeys
  20. from label import Label
  21. from untitled_icon import UntitledIcon
  22. from Emulator import MyEmulator
  23. from skin_manager import MySkinManager
  24. from lang_manager import MyLangManager
  25. from widget import Widget
  26. from counter_screen import CounterScreen
  27. class MessageBox(Label):
  28. _Parent = None
  29. def __init__(self):
  30. pass
  31. def Init(self,text,font_obj,color=MySkinManager.GiveColor('Text')):
  32. self._Color = color
  33. self._FontObj = font_obj
  34. self._Text = text
  35. self._Width = 0
  36. self._Height = 0
  37. self._CanvasHWND = pygame.Surface( ( int(self._Parent._Width),int(self._Parent._Height)))
  38. self._HWND = self._Parent._CanvasHWND
  39. def SetText(self,text):
  40. self._Text = MyLangManager.Tr(text)
  41. def PreDraw(self):
  42. self._Width = 0
  43. self._Height = 0
  44. self._CanvasHWND.fill(MySkinManager.GiveColor('White'))
  45. words = self._Text.split(' ')
  46. space = self._FontObj.size(' ')[0]
  47. max_width = self._Parent._Width-40
  48. x ,y = (0,0)
  49. row_total_width = 0
  50. lines = 0
  51. for word in words:
  52. word_surface = self._FontObj.render(word, True, self._Color)
  53. word_width = word_surface.get_width()
  54. word_height = word_surface.get_height()
  55. row_total_width += word_width
  56. if lines == 0:
  57. lines += word_height
  58. if row_total_width+space >= max_width:
  59. x = 0 # Reset the x.
  60. y += word_height # Start on new row.
  61. row_total_width = word_width
  62. lines += word_height
  63. self._CanvasHWND.blit(word_surface, (x, y))
  64. if len(words) == 1: # single line
  65. x += word_width
  66. else:
  67. x += word_width + space
  68. if x > self._Width:
  69. self._Width = x
  70. if lines >= (self._Parent._Height - 40):
  71. break
  72. self._Height = lines
  73. def DrawWith(self, x_,y_, withborder):
  74. self.PreDraw()
  75. x_ = x_ - self._Width/2
  76. y_ = y_ - self._Height/2
  77. padding = 5
  78. pygame.draw.rect(self._HWND,MySkinManager.GiveColor('White'),(x_-padding,y_-padding, self._Width+padding*2,self._Height+padding*2))
  79. if self._HWND != None:
  80. rect = pygame.Rect(x_,y_,self._Width,self._Height)
  81. self._HWND.blit(self._CanvasHWND,rect,(0,0,self._Width,self._Height))
  82. #self._HWND.blit(self._CanvasHWND,rect)
  83. if withborder == True:
  84. pygame.draw.rect(self._HWND,MySkinManager.GiveColor('Black'),(x_-padding,y_-padding, self._Width+padding*2,self._Height+padding*2),1)
  85. def Draw(self):
  86. x = (self._Parent._Width)/2
  87. y = (self._Parent._Height)/2
  88. self.DrawWith(x,y,True)
  89. python_package_flag = "__init__.py"
  90. emulator_flag = "action.config"
  91. ##Abstract object for manage Pages ,not the pygame's physic screen
  92. class MainScreen(Widget):
  93. _Pages = []
  94. _PageMax = 0
  95. _PageIndex = 0
  96. _PosY = TitleBar._BarHeight+1
  97. _Width = Width
  98. _Height = Height -FootBar._BarHeight -TitleBar._BarHeight
  99. _MyPageStack = None
  100. _CurrentPage = None # pointer to the current displaying Page Class
  101. _CanvasHWND = None
  102. _HWND = None
  103. _TitleBar = None
  104. _FootBar = None
  105. _MsgBox = None
  106. _MsgBoxFont = MyLangManager.TrFont("veramono20")
  107. _IconFont = MyLangManager.TrFont("varela15")
  108. _SkinManager = None
  109. _Closed = False
  110. _CounterScreen = None
  111. def __init__(self):
  112. self._Pages = []
  113. self._MyPageStack = PageStack()
  114. def Init(self):
  115. self._CanvasHWND = pygame.Surface((self._Width,self._Height))
  116. self._MsgBox= MessageBox()
  117. self._MsgBox._Parent= self
  118. self._MsgBox.Init(" ", self._MsgBoxFont)
  119. self._SkinManager = MySkinManager
  120. self._CounterScreen = CounterScreen()
  121. self._CounterScreen._HWND = self._HWND
  122. self._CounterScreen.Init()
  123. def FartherPages(self):
  124. self._PageMax = len(self._Pages)
  125. for i in range(0,self._PageMax):
  126. self._Pages[i]._Index = i
  127. self._Pages[i]._CanvasHWND = self._CanvasHWND
  128. self._Pages[i]._IconNumbers = len(self._Pages[i]._Icons)
  129. self._Pages[i]._Screen = self
  130. self._Pages[i].Adjust()
  131. if self._Pages[i]._IconNumbers > 1:
  132. self._Pages[i]._PsIndex = 1
  133. self._Pages[i]._IconIndex = self._Pages[i]._PsIndex
  134. self._CurrentPage = self._Pages[self._PageIndex]
  135. self._CurrentPage._OnShow = True
  136. def GetMyRightSidePage(self):
  137. ret = self._PageIndex +1
  138. if ret > (self._PageMax -1):
  139. ret = self._PageMax -1
  140. return ret
  141. def PageMoveLeft(self):
  142. self._Pages[self._PageIndex]._OnShow = False
  143. if self._PageIndex < (self._PageMax - 1):
  144. my_right_side_page = self.GetMyRightSidePage()
  145. for i in range(0,self._PageMax):
  146. if i!= self._PageIndex and i != my_right_side_page:
  147. self._Pages[i].MoveLeft(Width)
  148. self._Pages[self._PageIndex].EasingLeft(Width)
  149. if self._PageIndex != my_right_side_page:
  150. self._Pages[my_right_side_page].EasingLeft(Width)
  151. self._Pages[self._PageIndex].ResetPageSelector()
  152. self._PageIndex+=1
  153. if self._PageIndex > (self._PageMax -1):
  154. self._PageIndex = (self._PageMax -1)
  155. self._Pages[self._PageIndex]._OnShow = True
  156. self._CurrentPage = self._Pages[self._PageIndex]
  157. def GetMyLeftSidePage(self):
  158. ret = self._PageIndex -1
  159. if ret < 0:
  160. ret = 0
  161. return ret
  162. def PageMoveRight(self):
  163. self._Pages[self._PageIndex]._OnShow = False
  164. if self._PageIndex > 0:
  165. my_left_side_page = self.GetMyLeftSidePage()
  166. for i in range(0,self._PageMax):
  167. if i!= self._PageIndex and i!= my_left_side_page:
  168. pass
  169. #self._Pages[i].MoveRight(Width)
  170. self._Pages[self._PageIndex].EasingRight(Width)
  171. if self._PageIndex != my_left_side_page:
  172. self._Pages[my_left_side_page].EasingRight(Width)
  173. self._Pages[self._PageIndex].ResetPageSelector()
  174. self._PageIndex-=1
  175. if self._PageIndex < 0:
  176. self._PageIndex = 0
  177. self._Pages[self._PageIndex]._OnShow = True
  178. self._CurrentPage = self._Pages[self._PageIndex]
  179. def EasingAllPageLeft(self):
  180. current_time = 0.0
  181. start_posx = 0.0
  182. current_posx = start_posx
  183. final_posx = float(Width)
  184. posx_init = 0
  185. dur = 30
  186. last_posx = 0.0
  187. all_last_posx = []
  188. if self._PageIndex >= (self._PageMax - 1):
  189. return
  190. for i in range(0,Width*dur):
  191. current_posx = easing.SineIn(current_time,start_posx,final_posx-start_posx,float(dur))
  192. if current_posx >= final_posx:
  193. current_posx = final_posx
  194. dx = current_posx - last_posx
  195. all_last_posx.append(int(dx))
  196. current_time+=1
  197. last_posx = current_posx
  198. if current_posx >= final_posx:
  199. break
  200. c = 0
  201. for i in all_last_posx:
  202. c+=i
  203. if c < final_posx - start_posx:
  204. all_last_posx.append( final_posx - c )
  205. for i in all_last_posx:
  206. self.ClearCanvas()
  207. for j in self._Pages:
  208. j._PosX -= i
  209. j.DrawIcons()
  210. j._Screen.SwapAndShow()
  211. self._Pages[self._PageIndex]._OnShow = False
  212. self._PageIndex+=1
  213. if self._PageIndex > (self._PageMax -1):
  214. self._PageIndex = (self._PageMax -1)
  215. self._Pages[self._PageIndex]._OnShow = True
  216. self._CurrentPage = self._Pages[self._PageIndex]
  217. def EasingAllPageRight(self):
  218. current_time = 0.0
  219. start_posx = 0.0
  220. current_posx = start_posx
  221. final_posx = float(Width)
  222. posx_init = 0
  223. dur = 30
  224. last_posx = 0.0
  225. all_last_posx = []
  226. if self._PageIndex <= 0:
  227. return
  228. for i in range(0,Width*dur):
  229. current_posx = easing.SineIn(current_time,start_posx,final_posx-start_posx,float(dur))
  230. if current_posx >= final_posx:
  231. current_posx = final_posx
  232. dx = current_posx - last_posx
  233. all_last_posx.append(int(dx))
  234. current_time+=1
  235. last_posx = current_posx
  236. if current_posx >= final_posx:
  237. break
  238. c = 0
  239. for i in all_last_posx:
  240. c+=i
  241. if c < final_posx - start_posx:
  242. all_last_posx.append( final_posx - c )
  243. for i in all_last_posx:
  244. self.ClearCanvas()
  245. for j in reversed(self._Pages):
  246. j._PosX += i
  247. j.DrawIcons()
  248. j._Screen.SwapAndShow()
  249. self._Pages[self._PageIndex]._OnShow = False
  250. self._PageIndex-=1
  251. if self._PageIndex < 0:
  252. self._PageIndex = 0
  253. self._Pages[self._PageIndex]._OnShow = True
  254. self._CurrentPage = self._Pages[self._PageIndex]
  255. def CurPage(self):
  256. return self._CurrentPage
  257. def PushCurPage(self):
  258. self._MyPageStack.Push(self._CurrentPage)
  259. def SetCurPage(self,page):
  260. self._CurrentPage = page
  261. on_load_cb = getattr(self._CurrentPage,"OnLoadCb",None)
  262. if on_load_cb != None:
  263. if callable( on_load_cb ):
  264. self._CurrentPage.OnLoadCb()
  265. def PushPage(self,page):
  266. self.PushCurPage()
  267. self.SetCurPage(page)
  268. def AppendPage(self,Page):
  269. self._Pages.append(Page)
  270. def ClearCanvas(self):
  271. self._CanvasHWND.fill(self._SkinManager.GiveColor('White'))
  272. def SwapAndShow(self):
  273. if self._Closed == True:
  274. return
  275. if self._HWND != None:
  276. self._HWND.blit(self._CanvasHWND,(self._PosX,self._PosY,self._Width,self._Height))
  277. pygame.display.update()
  278. def ExtraName(self,name):
  279. ## extra name like 1_xxx to be => xxx,
  280. parts = name.split("_")
  281. if len(parts) > 1:
  282. return parts[1]
  283. elif len(parts) == 1:
  284. return parts[0]
  285. else:
  286. return name
  287. def IsExecPackage(self,dirname):
  288. files = os.listdir(dirname)
  289. bname = os.path.basename(dirname)
  290. bname = self.ExtraName(bname)
  291. for i in sorted(files):
  292. if i == bname+".sh":
  293. return True
  294. return False
  295. def IsEmulatorPackage(self,dirname):
  296. files = os.listdir(dirname)
  297. for i in sorted(files):
  298. if i.endswith(emulator_flag):
  299. return True
  300. return False
  301. def IsPythonPackage(self,dirname):
  302. files = os.listdir(dirname)
  303. for i in sorted(files):
  304. if i.endswith(python_package_flag):
  305. return True
  306. return False
  307. def ReunionPagesIcons(self): #This is for combining /home/cpi/apps/Menu and ~/launcher/Menu/GameShell
  308. for p in self._Pages:
  309. tmp = []
  310. for i,x in enumerate(p._Icons):
  311. tup = ('',0)
  312. if hasattr(x, '_FileName'):
  313. if str.find(x._FileName,"_") < 0:
  314. tup = ("98_"+x._FileName,i) # prefer to maintain PowerOFF in last position if the filename has no order labels
  315. else:
  316. tup = (x._FileName, i)
  317. else:
  318. tup = ("",i)
  319. tmp.append(tup)
  320. tmp = sorted(tmp, key=itemgetter(0))
  321. retro_games_idx = []
  322. retro_games_dir = "20_Retro Games"
  323. for i,x in enumerate(tmp):
  324. if retro_games_dir in x[0]:
  325. retro_games_idx.append(x[1])
  326. if len(retro_games_idx) > 1:
  327. for i in range(1,len(retro_games_idx)):
  328. p._Icons[retro_games_idx[0]]._LinkPage._Icons.extend( p._Icons[retro_games_idx[i]]._LinkPage._Icons) ### assumes the folder of ~/apps/Menu/20_Retro Games is legalzip","sfc"],
  329. tmp_swap = []
  330. for i, x in enumerate(tmp):
  331. if retro_games_dir not in x[0]:
  332. tmp_swap.append(x)
  333. if retro_games_dir in x[0] and i == retro_games_idx[0]:
  334. tmp_swap.append(x)
  335. tmp = tmp_swap
  336. #print(tmp)
  337. new_icons = []
  338. for x in tmp:
  339. new_icons.append( p._Icons[ x[1] ] )
  340. p._Icons = new_icons
  341. def ReadTheDirIntoPages(self,_dir,pglevel,cur_page):
  342. if FileExists(_dir) == False and os.path.isdir(_dir) == False:
  343. return
  344. files = os.listdir(_dir)
  345. for i in sorted(files):
  346. if os.path.isdir(_dir+"/"+i): # TOPLEVEL only is dir
  347. if pglevel == 0:
  348. page = Page()
  349. page._Name = self.ExtraName(i)
  350. page._Icons = []
  351. self._Pages.append(page)
  352. self.ReadTheDirIntoPages(_dir+"/"+i, pglevel+1 ,self._Pages[ len(self._Pages) -1])
  353. else: ## On CurPage now
  354. i2 = self.ExtraName(i)
  355. iconitem = IconItem()
  356. iconitem._FileName = i
  357. iconitem._CmdPath = ""
  358. iconitem.AddLabel(MyLangManager.Tr(i2),self._IconFont)
  359. if FileExists( _dir+"/"+i+"/"+i2+".png"): ### 20_Prog/Prog.png , cut 20_
  360. iconitem._ImageName = _dir+"/"+i+"/"+i2+".png"
  361. elif FileExists( SkinMap(_dir+"/"+i2+".png") ):
  362. iconitem._ImageName = SkinMap(_dir+"/"+i2+".png")
  363. else:
  364. untitled = UntitledIcon()
  365. untitled.Init()
  366. if len(i2) > 1:
  367. untitled.SetWords(i2[:2])
  368. elif len(i2) == 1:
  369. untitled.SetWords([i2[0],i2[0]])
  370. else:
  371. untitled.SetWords(["G","s"])
  372. iconitem._ImgSurf = untitled.Surface()
  373. iconitem._ImageName = ""
  374. if self.IsPythonPackage(_dir+"/"+i):
  375. iconitem._MyType = ICON_TYPES["FUNC"]
  376. sys.path.append(_dir)
  377. iconitem._CmdPath = __import__(i)
  378. init_cb = getattr(iconitem._CmdPath,"Init",None)
  379. if init_cb != None:
  380. if callable(init_cb):
  381. iconitem._CmdPath.Init(self)
  382. cur_page._Icons.append(iconitem)
  383. elif self.IsEmulatorPackage(_dir+"/"+i):
  384. obj = {}
  385. obj["ROM"] = ""
  386. obj["ROM_SO"] =""
  387. obj["EXT"] = []
  388. obj["EXCLUDE"] = []
  389. obj["FILETYPE"] = "file"
  390. obj["LAUNCHER"] = ""
  391. obj["TITLE"] = "Game"
  392. obj["SO_URL"] = ""
  393. obj["RETRO_CONFIG"] = "" ##
  394. try:
  395. f = open(_dir+"/"+i+"/"+emulator_flag)
  396. except IOError:
  397. print("action config open failed")
  398. return
  399. else:
  400. with f:
  401. content = f.readlines()
  402. content = [x.strip() for x in content]
  403. for c in content:
  404. pis = c.split("=")
  405. if len(pis) > 1:
  406. if "EXT" in pis[0]:
  407. obj[pis[0]] = pis[1].split(",")
  408. elif "EXCLUDE" in pis[0]:
  409. obj[pis[0]] = pis[1].split(",")
  410. else:
  411. obj[pis[0]] = pis[1]
  412. if FileExists(_dir+"/"+i+"/retroarch-local.cfg"):
  413. obj["RETRO_CONFIG"] = CmdClean(os.path.abspath( _dir+"/"+i+"/retroarch-local.cfg" ))
  414. print("a local retroarch cfg:", obj["RETRO_CONFIG"])
  415. em = MyEmulator()
  416. em._Emulator = obj
  417. em.Init(self)
  418. iconitem._CmdPath = em
  419. iconitem._MyType = ICON_TYPES["Emulator"]
  420. cur_page._Icons.append(iconitem)
  421. elif self.IsExecPackage(_dir+"/"+i):
  422. iconitem._MyType = ICON_TYPES["EXE"]
  423. iconitem._CmdPath = os.path.realpath(_dir+"/"+i+"/"+i2+".sh")
  424. MakeExecutable(iconitem._CmdPath)
  425. cur_page._Icons.append(iconitem)
  426. else:
  427. iconitem._MyType = ICON_TYPES["DIR"]
  428. iconitem._LinkPage = Page()
  429. iconitem._LinkPage._Name = i2
  430. cur_page._Icons.append(iconitem)
  431. self.ReadTheDirIntoPages(_dir+"/"+i,pglevel+1,iconitem._LinkPage)
  432. elif os.path.isfile(_dir+"/"+i) and pglevel > 0:
  433. if i.lower().endswith(icon_ext):
  434. i2 = self.ExtraName(i)
  435. #cmd = ReadTheFileContent(_dir+"/"+i)
  436. iconitem = IconItem()
  437. iconitem._FileName = i
  438. iconitem._CmdPath = os.path.realpath(_dir+"/"+i)
  439. MakeExecutable(iconitem._CmdPath)
  440. iconitem._MyType = ICON_TYPES["EXE"]
  441. if FileExists( SkinMap( _dir+"/"+ReplaceSuffix(i2,"png"))):
  442. iconitem._ImageName = SkinMap(_dir+"/"+ReplaceSuffix(i2,"png"))
  443. else:
  444. untitled = UntitledIcon()
  445. untitled.Init()
  446. if len(i2) > 1:
  447. untitled.SetWords(i2[:2])
  448. elif len(i2) == 1:
  449. untitled.SetWords([i2[0],i2[0]])
  450. else:
  451. untitled.SetWords(["G","s"])
  452. iconitem._ImgSurf = untitled.Surface()
  453. iconitem._ImageName = ""
  454. iconitem.AddLabel(MyLangManager.Tr(i2.split(".")[0]),self._IconFont)
  455. iconitem._LinkPage = None
  456. cur_page._Icons.append(iconitem)
  457. def RunEXE(self,cmdpath):
  458. self.DrawRun()
  459. self.SwapAndShow()
  460. pygame.time.delay(1000)
  461. cmdpath = cmdpath.strip()
  462. cmdpath = CmdClean(cmdpath)
  463. pygame.event.post( pygame.event.Event(RUNEVT, message=cmdpath))
  464. def OnExitCb(self,event):
  465. ## leave rest to Pages
  466. on_exit_cb = getattr(self._CurrentPage,"OnExitCb",None)
  467. if on_exit_cb != None:
  468. if callable( on_exit_cb ):
  469. self._CurrentPage.OnExitCb(event)
  470. return
  471. def KeyDown(self,event):
  472. """
  473. if event.key == pygame.K_PAGEUP:
  474. self.EasingAllPageLeft()
  475. #self.SwapAndShow()
  476. if event.key == pygame.K_PAGEDOWN:
  477. self.EasingAllPageRight()
  478. #self.SwapAndShow()
  479. """
  480. if event.key == pygame.K_t:
  481. self.DrawRun()
  482. self.SwapAndShow()
  483. """
  484. if event.key == CurKeys["Space"]:
  485. self._CounterScreen.Draw()
  486. self._CounterScreen.SwapAndShow()
  487. self._CounterScreen.StartCounter()
  488. """
  489. ## leave rest to Pages
  490. current_page_key_down_cb = getattr(self._CurrentPage,"KeyDown",None)
  491. if current_page_key_down_cb != None:
  492. if callable( current_page_key_down_cb ):
  493. self._CurrentPage.KeyDown(event)
  494. def DrawRun(self):
  495. self._MsgBox.SetText(MyLangManager.Tr("Launching"))
  496. self._MsgBox.Draw()
  497. def Draw(self):
  498. if self._Closed == True:
  499. return
  500. self._CurrentPage.Draw()
  501. #if self._HWND != None:
  502. # self._HWND.blit(self._CanvasHWND,(self._PosX,self._PosY,self._Width,self._Height))
  503. if self._TitleBar != None:
  504. self._TitleBar.Draw(self._CurrentPage._Name)
  505. if self._FootBar != None:
  506. if hasattr(self._CurrentPage,"_FootMsg"):
  507. self._FootBar.SetLabelTexts(self._CurrentPage._FootMsg)
  508. self._FootBar.Draw()