From f7c33a23a63d4ec70a539cc7c535b53191a18203 Mon Sep 17 00:00:00 2001 From: Jeremy Penner Date: Sun, 5 Jul 2020 22:37:32 -0400 Subject: [PATCH] Add tooltip sidebar, auto-select first item in dropdown with enter --- .gitignore | 3 +- scripting.py | 119 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index 3cfe9b3..a62ea4c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ build *.h *.marm *.marm.* -*.so \ No newline at end of file +*.so +.vscode \ No newline at end of file diff --git a/scripting.py b/scripting.py index bc49924..47ae961 100644 --- a/scripting.py +++ b/scripting.py @@ -54,8 +54,7 @@ class Defs(TPrs): class Vm(TPrs): def InitPersistent(self, defs): - self.rgflagdef = defs.rgflagdef - self.rgbotdef = defs.rgbotdef + self.defs = defs self.mpflagdef = { flagdef: flagdef.value for flagdef in defs.rgflagdef } self.mpbotdef = { botdef: Rbot(botdef) for botdef in defs.rgbotdef } def SetFlag(self, flagdef, fValue): @@ -147,25 +146,46 @@ class Synt(Typeable): def SyntDefault(cls): return cls() - def ProjectTypein(self, pcur): - return self.syntParent.ProjectTypeinForChild(pcur.defs, pcur.PwHoriz(self), self, self.StForTypein()) + def Populate(self): + "Populate rgsynt with useful default values." + 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) - def ProjectTypeinForChild(self, defs, pwParent, syntChild, st): + 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): self.Replace(syntChild, synt) syntChild.SetStTypein(None) if syntChild.GetStTypein() != None: pwParent = PwDropdown(pwParent) - PwTypein(pwParent, st, syntChild) - if syntChild.GetStTypein() != None: - stype = self.StypeForChild(syntChild) - if stype: - for synt in stype.RgsyntFromSt(defs, syntChild.GetStTypein()): + pwTypein = PwTypein(pwParent, st, syntChild) + stype = self.StypeForChild(syntChild) + if stype: + 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) + pcur.pwTooltip.AddTip(pwParent, Tooltip) + else: + pcur.pwTooltip.AddTip(pwTypein, Tooltip) - def StypeForChild(self, syntChild): - return None - def Replace(self, syntOld, syntNew): assert syntNew.syntParent == None syntNew.syntParent = self @@ -180,17 +200,10 @@ class Synt(Typeable): if isinstance(syntChild, int): return syntChild return self.rgsynt.index(syntChild) - - def Populate(self): - "Populate rgsynt with useful default values." - 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) + + def Stype(self): + return self.syntParent.stypeForChild(self) if self.syntParent else None + # desc - list of desces # desce: @@ -283,8 +296,6 @@ class SyntBlock(Synt): def Project(self, pcur): with pcur.Indent(2 if pcur.pwHoriz != None else 0): pcur.EndLine() - if pcur.pwVert == None: - pcur.pwVert = PwBlock(None, self, pcur.dxindent) def OnInsertNewLine(syntAfter, psel): self.InsertLineAfter(syntAfter, None) psel.Inc(pcur.pwVert) # does nothing because we need to reproject @@ -420,14 +431,16 @@ class Fail(TPrs): class Pcur(object): @staticmethod def PwProjected(defs, synt): - pcur = Pcur(defs) + pcur = Pcur(defs, synt) synt.Project(pcur) - return pcur.pwVert + return pcur.pwSplit - def __init__(self, defs): + def __init__(self, defs, synt): self.defs = defs - self.pwVert = None + self.pwSplit = PwSplit(None, 80) + self.pwVert = PwBlock(self.pwSplit, synt, 0) self.pwHoriz = None + self.pwTooltip = PwTooltip(self.pwSplit) self.dxindent = 0 @contextmanager @@ -508,7 +521,7 @@ class PwContainOne(Pw): return [self.pwChild] def FContainer(self): return True - + class PwExpand(PwContainOne): def DxDyNew(self, w, dxStart, mpksel): dxdy = self.pwChild.DxDyNew(w, dxStart, mpksel) @@ -519,7 +532,36 @@ class PwExpand(PwContainOne): if not fOverlay: 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) - + +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): def __init__(self, pwParent, stText): Pw.__init__(self, pwParent) @@ -681,6 +723,11 @@ class PwDropdown(PwBlock): return PwBlock.RgpwChild(self)[0] def DxDyNew(self, 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): if not fOverlay: self.PwChildFirst().Draw(ascr, x, y, w, dxStart, mpksel, False) @@ -866,15 +913,21 @@ class PselState(LeaveJoinToken): class Mpksel(object): def __init__(self, pwRoot, client, mpclient_psel): self.mppw_ksel = {} + self.pwNav = None for clientT, psel in mpclient_psel.items(): kselNew = Ksel.NAV + pw = psel.PwSelected(pwRoot) if clientT != client: kselNew = kselNew | Ksel.FOTHER - pw = psel.PwSelected(pwRoot) + else: + self.pwNav = pw self.mppw_ksel[pw] = Ksel.KselBest(kselNew, self.Get(pw)) def Get(self, pw): return self.mppw_ksel.get(pw, Ksel.NONE) + + def GetPwNav(self): + return self.pwNav def GetMpksel(self, pwRoot, client): return self.Mpksel(pwRoot, client, self.mpclient_psel)