Add tooltip sidebar, auto-select first item in dropdown with enter

This commit is contained in:
Jeremy Penner 2020-07-05 22:37:32 -04:00
parent 4e720f0d67
commit f7c33a23a6
2 changed files with 88 additions and 34 deletions

1
.gitignore vendored
View file

@ -9,3 +9,4 @@ build
*.marm *.marm
*.marm.* *.marm.*
*.so *.so
.vscode

View file

@ -54,8 +54,7 @@ class Defs(TPrs):
class Vm(TPrs): class Vm(TPrs):
def InitPersistent(self, defs): def InitPersistent(self, defs):
self.rgflagdef = defs.rgflagdef self.defs = defs
self.rgbotdef = defs.rgbotdef
self.mpflagdef = { flagdef: flagdef.value for flagdef in defs.rgflagdef } self.mpflagdef = { flagdef: flagdef.value for flagdef in defs.rgflagdef }
self.mpbotdef = { botdef: Rbot(botdef) for botdef in defs.rgbotdef } self.mpbotdef = { botdef: Rbot(botdef) for botdef in defs.rgbotdef }
def SetFlag(self, flagdef, fValue): def SetFlag(self, flagdef, fValue):
@ -147,24 +146,45 @@ class Synt(Typeable):
def SyntDefault(cls): def SyntDefault(cls):
return cls() return cls()
def ProjectTypein(self, pcur): def Populate(self):
return self.syntParent.ProjectTypeinForChild(pcur.defs, pcur.PwHoriz(self), self, self.StForTypein()) "Populate rgsynt with useful default values."
pass
def ProjectTypeinForChild(self, defs, pwParent, syntChild, st): def Project(self, pcur):
"Project the current synt into its corresponding pws."
pass
def Eval(self, vm):
"Execute yourself, in the context of vm."
return Fail("Missing information", self)
def StypeForChild(self, syntChild):
"Return the stype that should be applied to the given child."
return None
def ProjectTypein(self, pcur):
return self.syntParent.ProjectTypeinForChild(pcur, self, self.StForTypein())
def ProjectTypeinForChild(self, pcur, syntChild, st):
pwParent = pcur.PwHoriz(syntChild)
def OnSelect(synt, psel): def OnSelect(synt, psel):
self.Replace(syntChild, synt) self.Replace(syntChild, synt)
syntChild.SetStTypein(None) syntChild.SetStTypein(None)
if syntChild.GetStTypein() != None: if syntChild.GetStTypein() != None:
pwParent = PwDropdown(pwParent) pwParent = PwDropdown(pwParent)
PwTypein(pwParent, st, syntChild) pwTypein = PwTypein(pwParent, st, syntChild)
if syntChild.GetStTypein() != None:
stype = self.StypeForChild(syntChild) stype = self.StypeForChild(syntChild)
if stype: if stype:
for synt in stype.RgsyntFromSt(defs, syntChild.GetStTypein()): def Tooltip():
pw = PwBlock(None, None, 0)
for synt in stype.RgsyntFromSt(pcur.defs, ''):
PwStatic(pw, synt.StForTypein())
return pw
if syntChild.GetStTypein() != None:
for synt in stype.RgsyntFromSt(pcur.defs, syntChild.GetStTypein()):
PwButton(pwParent, synt.StForTypein(), synt, OnSelect) PwButton(pwParent, synt.StForTypein(), synt, OnSelect)
pcur.pwTooltip.AddTip(pwParent, Tooltip)
def StypeForChild(self, syntChild): else:
return None pcur.pwTooltip.AddTip(pwTypein, Tooltip)
def Replace(self, syntOld, syntNew): def Replace(self, syntOld, syntNew):
assert syntNew.syntParent == None assert syntNew.syntParent == None
@ -181,16 +201,9 @@ class Synt(Typeable):
return syntChild return syntChild
return self.rgsynt.index(syntChild) return self.rgsynt.index(syntChild)
def Populate(self): def Stype(self):
"Populate rgsynt with useful default values." return self.syntParent.stypeForChild(self) if self.syntParent else None
pass
def Project(self, pcur):
"Project the current synt into its corresponding pws."
pass
def Eval(self, vm):
"Execute yourself, in the context of vm."
return Fail("Missing information", self)
# desc - list of desces # desc - list of desces
# desce: # desce:
@ -283,8 +296,6 @@ class SyntBlock(Synt):
def Project(self, pcur): def Project(self, pcur):
with pcur.Indent(2 if pcur.pwHoriz != None else 0): with pcur.Indent(2 if pcur.pwHoriz != None else 0):
pcur.EndLine() pcur.EndLine()
if pcur.pwVert == None:
pcur.pwVert = PwBlock(None, self, pcur.dxindent)
def OnInsertNewLine(syntAfter, psel): def OnInsertNewLine(syntAfter, psel):
self.InsertLineAfter(syntAfter, None) self.InsertLineAfter(syntAfter, None)
psel.Inc(pcur.pwVert) # does nothing because we need to reproject psel.Inc(pcur.pwVert) # does nothing because we need to reproject
@ -420,14 +431,16 @@ class Fail(TPrs):
class Pcur(object): class Pcur(object):
@staticmethod @staticmethod
def PwProjected(defs, synt): def PwProjected(defs, synt):
pcur = Pcur(defs) pcur = Pcur(defs, synt)
synt.Project(pcur) synt.Project(pcur)
return pcur.pwVert return pcur.pwSplit
def __init__(self, defs): def __init__(self, defs, synt):
self.defs = defs self.defs = defs
self.pwVert = None self.pwSplit = PwSplit(None, 80)
self.pwVert = PwBlock(self.pwSplit, synt, 0)
self.pwHoriz = None self.pwHoriz = None
self.pwTooltip = PwTooltip(self.pwSplit)
self.dxindent = 0 self.dxindent = 0
@contextmanager @contextmanager
@ -520,6 +533,35 @@ class PwExpand(PwContainOne):
ascr.PutSt(" " * w, x, y, self.pwChild.ColFg(), self.pwChild.ColBg(mpksel.Get(self.pwChild))) ascr.PutSt(" " * w, x, y, self.pwChild.ColFg(), self.pwChild.ColBg(mpksel.Get(self.pwChild)))
self.pwChild.Draw(ascr, x, y, w, dxStart, mpksel, fOverlay) self.pwChild.Draw(ascr, x, y, w, dxStart, mpksel, fOverlay)
class PwTooltip(Pw):
def __init__(self, pwParent):
Pw.__init__(self, pwParent)
self.mppw_tip = {}
def AddTip(self, pw, tip):
self.mppw_tip[pw] = tip
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
pwNav = mpksel.GetPwNav()
while pwNav:
tip = self.mppw_tip.get(pwNav)
if tip:
tip().Draw(ascr, x, y, w, dxStart, mpksel, fOverlay)
break
else:
pwNav = pwNav.pwParent
class PwSplit(PwContainer):
def __init__(self, pwParent, pctLeft):
PwContainer.__init__(self, pwParent)
self.pctLeft = pctLeft
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
assert len(self.RgpwChild()) == 2
wLeft = (self.pctLeft * w) / 100
wRight = w - wLeft - 1
self.RgpwChild()[0].Draw(ascr, x, y, wLeft, dxStart, mpksel, fOverlay)
self.RgpwChild()[1].Draw(ascr, x + wLeft + 1, y, wRight, 0, mpksel, fOverlay)
if not fOverlay:
ascr.Fill(ansi.MkAch(chr(186), ansi.WHITE | ansi.FBRIGHT, ansi.BLACK), 1, ascr.H() - y + 1, x + wLeft, y)
class PwStatic(Pw): class PwStatic(Pw):
def __init__(self, pwParent, stText): def __init__(self, pwParent, stText):
Pw.__init__(self, pwParent) Pw.__init__(self, pwParent)
@ -681,6 +723,11 @@ class PwDropdown(PwBlock):
return PwBlock.RgpwChild(self)[0] return PwBlock.RgpwChild(self)[0]
def DxDyNew(self, w, dxStart, mpksel): def DxDyNew(self, w, dxStart, mpksel):
return self.PwChildFirst().DxDyNew(w, dxStart, mpksel) return self.PwChildFirst().DxDyNew(w, dxStart, mpksel)
def HandleKey(self, pov, psel, key):
if (key == ansi.K_RETURN or key == ansi.K_NEWLINE) and len(self.RgpwChild()) > 1:
return self.RgpwChild()[1].HandleKey(pov, psel, key)
return PwBlock.HandleKey(self, pov, psel, key)
def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay): def Draw(self, ascr, x, y, w, dxStart, mpksel, fOverlay):
if not fOverlay: if not fOverlay:
self.PwChildFirst().Draw(ascr, x, y, w, dxStart, mpksel, False) self.PwChildFirst().Draw(ascr, x, y, w, dxStart, mpksel, False)
@ -866,16 +913,22 @@ class PselState(LeaveJoinToken):
class Mpksel(object): class Mpksel(object):
def __init__(self, pwRoot, client, mpclient_psel): def __init__(self, pwRoot, client, mpclient_psel):
self.mppw_ksel = {} self.mppw_ksel = {}
self.pwNav = None
for clientT, psel in mpclient_psel.items(): for clientT, psel in mpclient_psel.items():
kselNew = Ksel.NAV kselNew = Ksel.NAV
pw = psel.PwSelected(pwRoot)
if clientT != client: if clientT != client:
kselNew = kselNew | Ksel.FOTHER kselNew = kselNew | Ksel.FOTHER
pw = psel.PwSelected(pwRoot) else:
self.pwNav = pw
self.mppw_ksel[pw] = Ksel.KselBest(kselNew, self.Get(pw)) self.mppw_ksel[pw] = Ksel.KselBest(kselNew, self.Get(pw))
def Get(self, pw): def Get(self, pw):
return self.mppw_ksel.get(pw, Ksel.NONE) return self.mppw_ksel.get(pw, Ksel.NONE)
def GetPwNav(self):
return self.pwNav
def GetMpksel(self, pwRoot, client): def GetMpksel(self, pwRoot, client):
return self.Mpksel(pwRoot, client, self.mpclient_psel) return self.Mpksel(pwRoot, client, self.mpclient_psel)
def PselByClient(self, client): def PselByClient(self, client):