pymenu.py

Created by zetamap

Created on March 20, 2024

2.27 KB

PyMenu

The new library for making beautiful menus on numworks with dynamic accent colors. Thanks to cent20’s library, menu.py, for inspiring me for this library.

To give a little aspect, here is a demo script showing the options menu of my game snake_lite.

How to use

This is simple, you just need to create an instance of Menu class and set menu title, menu confirm label, and options, like this :
menu = Menu(
"My Title", # Replace by your menu title (e.g. "Game Options")
"Close menu", # Replace by your menu confirm label (e.g. "Start Game")
[ # And here is the options of menu.
#Must be formatted like this: ["<option label>", "<default value>", <values...>],
["My option", 0, "value 1", 2, [0,1]],
])
Note: The default option value must be an index of the list of values, not the value itself.

And after, just open the menu like this:
menu.open()

This will automatically clear the screen, format and display options, add a scrollbar if list exceed 6 options per page, handle keys to move through the menu, clear again the screen at end and return the result of selected options.
You can open the menu as many times you want by recalling the function, the last selected option will be automatically focused.

Full documentation here


"""Documentation: https://github.com/ZetaMap/PyMenu"""
from kandinsky import fill_rect as fr,draw_string as ds
from ion import keydown as kd

def wait_key(*k):
 while k:
  for i in k:
   if kd(i):
    while kd(i):pass
    return i
def accent_colors():
 c=["#ffb531","1.0"]
 try:
  from ion import get_keys
  c[0]="#c53431"
 except:
  try:
   from kandinsky import get_palette as gp
   c=[gp()["Toolbar"],gp()["HomeBackground"]]
  except:pass
 return c
class Menu:
 colors=["0.95","0.2","0.38",accent_colors()[0],"#2a78e0"]
 r,u=colors,1
 options=lambda s:[s.p[i][s.c[i]]for i in range(s.s)]
 def __init__(s,title,action,*options):
  s.t,s.a,s.p=title[0:25],action[0:28],options
  s.s=len(s.p)
  s.c,s.b=[],len(s.a)
  for i in s.p :
   assert len(i)>2,"need [label,default,values...]"
   s.c.append(int(i[1])+2)
 def mo(s,i,o,u):
  t,l=str(s.p[i][s.c[i]])[0:15],i%6
  ds('<'*u+' '*(17-2*u)+'>'*u,140,65+l*25,s.r[2],s.r[1])
  ds(t,150+(150-10*len(t))//2,65+l*25,o,s.r[1])
 def do(s):
  u=s.u-1*(s.u>0)
  u=(u-(u and u==u//6*6))//6*6
  for i in range(6):
   if u+i<s.s:
    t=str(s.p[u+i][0])[0:12]
    ds(t+' '*(12-len(t)),10,65+i*25,s.r[0],s.r[1])
    s.mo(u+i,s.r[2],0)
   else:fr(10,65+i*25,300,18,s.r[1])
  if s.s>6:s.sb(u)
 def sb(s,u):
  fr(314,65,3,140,s.r[2])
  o=140//((s.s+5)//6)
  p=o*(u//6)
  fr(314,65+p,3,o+(u==s.s//6*6)*(140-o-p),s.r[3])
 def da(s,i):
  c=s.r[3*(i==0)]
  ds("<",10,8,c,s.r[1])
  fr(13,16,13,1,c)
  fr(26,10,1,6,c)
  ds(s.a,160-5*s.b,36,s.r[3*(i==1)],s.r[1])
 def ds(s):
  fr(0,0,320,222,s.r[1])
  ds(s.t,160-5*len(s.t),8,s.r[4],s.r[1])
  s.da(s.u)
  s.do()
 def close(s,b=0):
  fr(0,0,320,222,s.r[1])
  if b:s.u=1
  return[]if b else s.options()
 def open(s):
  s.ds()
  r=-1
  while 1:
   if r in(5,17):return s.close(1)
   elif r in(4,52):
    if s.u<2:return s.close(s.u==0)
    r=-2
   elif r in(-1,1,2):
    if s.u<2:s.da(s.u+1)
    s.u=(s.u-1*(r==1)+1*(r==2))%(s.s+2)
    s.do()
    if s.u>1:s.mo(s.u-2,s.r[3],1)
    else:s.da(s.u)
   if r in(-2,0,3)and s.u>1:
    v=s.u-2
    s.c[v],l=s.c[v]+1*(r==3)-1*(r==0)if r>=0 else s.p[v][1]+2,s.c[v]
    o=s.c[v]
    if o==1:s.c[v]=len(s.p[v])-1
    if o==len(s.p[v]):s.c[v]=2
    if l!=o and s.p[v][0].lower()=="darkmode":#special
     s.r[0],s.r[1]=s.r[1],s.r[0]
     s.ds()
    s.mo(v,s.r[3],1)
   r=wait_key(0,1,2,3,4,5,17,52)