Skip to content

Commit

Permalink
Consolidation of termshark's menu code
Browse files Browse the repository at this point in the history
I was accumulating menus in the widget hierarchy, so made things
simpler. Instead there is now a struct called multiMenu which is
essentially a menu holder. Other parts of termshark that want to open a
menu are now provided with a IMenuOpener type. This is implemented by
the openTermsharkMenu() function. This indirection is needed because the
menu being opened has to be provided with the widget rendering the rest
of the UI underneath because the menu widget needs to render the
underlying widget so that it can inspect the canvas to find the
coordinates at which it should open. So the IMenuOpener provides the
link between the widget and the application it's opening inside, and so
behind the scenes will adjust the menu being opened with the correct
lower layer.

Note that multiMenu only supports one open menu at a time. The
conversations UI allows a cascaded menu to be opened, so I'm doing that
the old way for now.
  • Loading branch information
gcla committed Feb 22, 2021
1 parent f265ddf commit 851cb7b
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 51 deletions.
20 changes: 11 additions & 9 deletions ui/menuutil/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/gcla/gowid/widgets/styled"
"github.com/gcla/gowid/widgets/text"
"github.com/gcla/gowid/widgets/vpadding"
"github.com/gcla/termshark/v2/widgets"
"github.com/gcla/termshark/v2/widgets/appkeys"
"github.com/gdamore/tcell"
)
Expand Down Expand Up @@ -186,11 +187,12 @@ func makeMenuWithHotKeys(items []SimpleMenuItem, showKeys bool) (gowid.IWidget,
//======================================================================

type NextMenu struct {
Cur *menu.Widget
Next *menu.Widget // nil if menu is nil
Site *menu.SiteWidget
Container gowid.IFocus // container holding menu buttons, etc
Focus int // index of next menu in container
Cur *menu.Widget
Next *menu.Widget // nil if menu is nil
Site *menu.SiteWidget
Container gowid.IFocus // container holding menu buttons, etc
Focus int // index of next menu in container
MenuOpener widgets.IMenuOpener // For integrating with UI app - the menu needs to be told what's underneath when opened
}

func MakeMenuNavigatingKeyPress(left *NextMenu, right *NextMenu) appkeys.KeyInputFn {
Expand All @@ -204,15 +206,15 @@ func MenuNavigatingKeyPress(evk *tcell.EventKey, left *NextMenu, right *NextMenu
switch evk.Key() {
case tcell.KeyLeft:
if left != nil {
left.Cur.Close(app)
left.Next.Open(left.Site, app)
left.MenuOpener.CloseMenu(left.Cur, app)
left.MenuOpener.OpenMenu(left.Next, left.Site, app)
left.Container.SetFocus(app, left.Focus) // highlight next menu selector
res = true
}
case tcell.KeyRight:
if right != nil {
right.Cur.Close(app)
right.Next.Open(right.Site, app)
right.MenuOpener.CloseMenu(right.Cur, app)
right.MenuOpener.OpenMenu(right.Next, right.Site, app)
right.Container.SetFocus(app, right.Focus) // highlight next menu selector
res = true
}
Expand Down
4 changes: 3 additions & 1 deletion ui/streamui.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/gcla/termshark/v2/pcap"
"github.com/gcla/termshark/v2/pdmltree"
"github.com/gcla/termshark/v2/streams"
"github.com/gcla/termshark/v2/widgets"
"github.com/gcla/termshark/v2/widgets/appkeys"
"github.com/gcla/termshark/v2/widgets/streamwidget"
"github.com/gdamore/tcell"
Expand Down Expand Up @@ -319,7 +320,7 @@ func (t *streamParseHandler) TrackPayloadPacket(packet int) {

func (t *streamParseHandler) OnStreamHeader(hdr streams.FollowHeader) {
t.app.Run(gowid.RunFunction(func(app gowid.IApp) {
t.wid.AddHeader(hdr)
t.wid.AddHeader(hdr, app)
}))
}

Expand Down Expand Up @@ -439,6 +440,7 @@ func makeStreamWidget(previousFilter string, filter string, cap string, proto st
return streamwidget.New(filter, cap, proto,
conversationMenu, conversationMenuHolder, &keyState,
streamwidget.Options{
MenuOpener: widgets.MenuOpenerFunc(openTermsharkMenu),
DefaultDisplay: func() streamwidget.DisplayFormat {
view := streamwidget.Hex
choice := termshark.ConfString("main.stream-view", "hex")
Expand Down
99 changes: 69 additions & 30 deletions ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ var savedListBoxWidgetHolder *holder.Widget
var singlePacketViewMsgHolder *holder.Widget // either empty or "loading..."
var keyMapper *mapkeys.Widget

type MenuHolder struct {
gowid.IMenuCompatible
}

var multiMenu *MenuHolder = &MenuHolder{}
var multiMenuWidget *holder.Widget

var tabViewsForward map[gowid.IWidget]gowid.IWidget
var tabViewsBackward map[gowid.IWidget]gowid.IWidget

Expand Down Expand Up @@ -218,6 +225,35 @@ func (g getMappings) None() bool {
return len(termshark.LoadKeyMappings()) == 0
}

//======================================================================

// openTermsharkMenu implements widgets.IMenuHolder. It is used to integrate widgets
// that need to open menus into the overall application. This is needed because the menu
// widget needs to be rendered first - specifically the screen under the menu - so that
// the menu can read out of the resulting canvas the coordinates on the screen at which
// it should open.
func openTermsharkMenu(open bool, m *menu.Widget, site *menu.SiteWidget, app gowid.IApp) bool {
if open {
if multiMenu.IMenuCompatible != m {
multiMenu.IMenuCompatible = m
m.SetSubWidget(appView, app)
m.Open(site, app)
app.Redraw()
return true
} else {
return false
}
} else {
if multiMenu.IMenuCompatible == m {
m.Close(app)
multiMenu.IMenuCompatible = holder.New(appView)
return true
} else {
return false
}
}
}

func RequestQuit() {
select {
case QuitRequestedChan <- struct{}{}:
Expand Down Expand Up @@ -1680,7 +1716,7 @@ func focusOnMenuButton(app gowid.IApp) {

func openGeneralMenu(app gowid.IApp) {
focusOnMenuButton(app)
generalMenu.Open(openMenuSite, app)
openTermsharkMenu(true, generalMenu, openMenuSite, app)
}

// Keys for the whole app, applicable whichever view is frontmost
Expand Down Expand Up @@ -2494,7 +2530,7 @@ func makeRecentMenuWidget() (gowid.IWidget, int) {
Txt: s,
Key: gowid.MakeKey('a' + rune(i)),
CB: func(app gowid.IApp, w gowid.IWidget) {
savedMenu.Close(app)
openTermsharkMenu(false, savedMenu, nil, app)
// capFilter global, set up in cmain()
RequestLoadPcapWithCheck(scopy, FilterWidget.Value(), NoGlobalJump, app)
},
Expand Down Expand Up @@ -2722,7 +2758,7 @@ func Build() (*gowid.App, error) {

openMenuSite = menu.NewSite(menu.SiteOptions{YOffset: 1})
openMenu.OnClick(gowid.MakeWidgetCallback(gowid.ClickCB{}, func(app gowid.IApp, target gowid.IWidget) {
generalMenu.Open(openMenuSite, app)
openTermsharkMenu(true, generalMenu, openMenuSite, app)
}))

//======================================================================
Expand All @@ -2734,7 +2770,7 @@ func Build() (*gowid.App, error) {
Txt: "Refresh Screen",
Key: gowid.MakeKeyExt2(0, tcell.KeyCtrlL, ' '),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
app.Sync()
},
},
Expand All @@ -2743,7 +2779,7 @@ func Build() (*gowid.App, error) {
Txt: "Toggle Dark Mode",
Key: gowid.MakeKey('d'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
DarkMode = !DarkMode
termshark.SetConf("main.dark-mode", DarkMode)
},
Expand All @@ -2753,15 +2789,15 @@ func Build() (*gowid.App, error) {
Txt: "Clear Packets",
Key: gowid.MakeKeyExt2(0, tcell.KeyCtrlW, ' '),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
reallyClear(app)
},
},
menuutil.SimpleMenuItem{
Txt: "Edit Columns",
Key: gowid.MakeKey('e'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
openEditColumns(app)
},
}}...)
Expand All @@ -2771,7 +2807,7 @@ func Build() (*gowid.App, error) {
Txt: "Show Log",
Key: gowid.MakeKey('l'),
CB: func(app gowid.IApp, w gowid.IWidget) {
analysisMenu.Close(app)
openTermsharkMenu(false, analysisMenu, nil, app)
openLogsUi(app)
},
})
Expand All @@ -2783,15 +2819,15 @@ func Build() (*gowid.App, error) {
Txt: "Help",
Key: gowid.MakeKey('?'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
OpenTemplatedDialog(appView, "UIHelp", app)
},
},
menuutil.SimpleMenuItem{
Txt: "User Guide",
Key: gowid.MakeKey('u'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
if !termshark.RunningRemotely() {
termshark.BrowseUrl(termshark.UserGuideURL)
}
Expand All @@ -2802,7 +2838,7 @@ func Build() (*gowid.App, error) {
Txt: "FAQ",
Key: gowid.MakeKey('f'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
if !termshark.RunningRemotely() {
termshark.BrowseUrl(termshark.FAQURL)
}
Expand All @@ -2814,7 +2850,7 @@ func Build() (*gowid.App, error) {
Txt: "Found a Bug?",
Key: gowid.MakeKey('b'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
if !termshark.RunningRemotely() {
termshark.BrowseUrl(termshark.BugURL)
}
Expand All @@ -2825,7 +2861,7 @@ func Build() (*gowid.App, error) {
Txt: "Feature Request?",
Key: gowid.MakeKey('f'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
if !termshark.RunningRemotely() {
termshark.BrowseUrl(termshark.FeatureURL)
}
Expand All @@ -2837,7 +2873,7 @@ func Build() (*gowid.App, error) {
Txt: "Quit",
Key: gowid.MakeKey('q'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
reallyQuit(app)
},
},
Expand All @@ -2852,7 +2888,7 @@ func Build() (*gowid.App, error) {
Txt: "Toggle Packet Colors",
Key: gowid.MakeKey('c'),
CB: func(app gowid.IApp, w gowid.IWidget) {
generalMenu.Close(app)
openTermsharkMenu(false, generalMenu, nil, app)
PacketColors = !PacketColors
termshark.SetConf("main.packet-colors", PacketColors)
},
Expand Down Expand Up @@ -2900,31 +2936,31 @@ func Build() (*gowid.App, error) {

openAnalysisSite = menu.NewSite(menu.SiteOptions{XOffset: -12, YOffset: 1})
openAnalysis.OnClick(gowid.MakeWidgetCallback(gowid.ClickCB{}, func(app gowid.IApp, target gowid.IWidget) {
analysisMenu.Open(openAnalysisSite, app)
openTermsharkMenu(true, analysisMenu, openAnalysisSite, app)
}))

analysisMenuItems := []menuutil.SimpleMenuItem{
menuutil.SimpleMenuItem{
Txt: "Capture file properties",
Key: gowid.MakeKey('p'),
CB: func(app gowid.IApp, w gowid.IWidget) {
analysisMenu.Close(app)
openTermsharkMenu(false, analysisMenu, nil, app)
startCapinfo(app)
},
},
menuutil.SimpleMenuItem{
Txt: "Reassemble stream",
Key: gowid.MakeKey('f'),
CB: func(app gowid.IApp, w gowid.IWidget) {
analysisMenu.Close(app)
openTermsharkMenu(false, analysisMenu, nil, app)
startStreamReassembly(app)
},
},
menuutil.SimpleMenuItem{
Txt: "Conversations",
Key: gowid.MakeKey('c'),
CB: func(app gowid.IApp, w gowid.IWidget) {
analysisMenu.Close(app)
openTermsharkMenu(false, analysisMenu, nil, app)
openConvsUi(app)
},
},
Expand Down Expand Up @@ -3015,13 +3051,15 @@ func Build() (*gowid.App, error) {
generalNext.Next = analysisMenu
generalNext.Site = openAnalysisSite
generalNext.Container = titleCols
generalNext.MenuOpener = widgets.MenuOpenerFunc(openTermsharkMenu)
generalNext.Focus = 4 // should really find by ID

// <<generalmenu2>>
analysisNext.Cur = analysisMenu
analysisNext.Next = generalMenu
analysisNext.Site = openMenuSite
analysisNext.Container = titleCols
analysisNext.MenuOpener = widgets.MenuOpenerFunc(openTermsharkMenu)
analysisNext.Focus = 6 // should really find by ID

packetListViewHolder = holder.New(nullw)
Expand All @@ -3041,8 +3079,9 @@ func Build() (*gowid.App, error) {
),
)

FilterWidget = filter.New(filter.Options{
Completer: savedCompleter{def: termshark.NewFields()},
FilterWidget = filter.New("filter", filter.Options{
Completer: savedCompleter{def: termshark.NewFields()},
MenuOpener: widgets.MenuOpenerFunc(openTermsharkMenu),
})

validFilterCb := gowid.MakeWidgetCallback("cb", func(app gowid.IApp, w gowid.IWidget) {
Expand Down Expand Up @@ -3076,7 +3115,7 @@ func Build() (*gowid.App, error) {
)
savedBtnSite := menu.NewSite(menu.SiteOptions{YOffset: 1})
savedw.OnClick(gowid.MakeWidgetCallback("cb", func(app gowid.IApp, w gowid.IWidget) {
savedMenu.Open(savedBtnSite, app)
openTermsharkMenu(true, savedMenu, savedBtnSite, app)
}))

progWidgetIdx = 7 // adjust this if nullw moves position in filterCols
Expand Down Expand Up @@ -3492,20 +3531,20 @@ func Build() (*gowid.App, error) {
appView = holder.New(mbView)
}

// A restriction on the multiMenu is that it only holds one open menu, so using
// this trick, only one menu can be open at a time.
multiMenu.IMenuCompatible = holder.New(appView)

var lastMenu gowid.IWidget = appView
menus := []gowid.IMenuCompatible{
savedMenu,
analysisMenu,
generalMenu,
conversationMenu,
// These menus can both be open at the same time, so I have special
// handling here. I should use a more general method for all menus. The
// current method only allows one menu to be open at a time.
filterConvsMenu1,
filterConvsMenu2,
colNamesMenu,
colFieldsMenu,
multiMenu,
}

menus = append(menus, FilterWidget.Menus()...)

for _, w := range menus {
w.SetSubWidget(lastMenu, app)
lastMenu = w
Expand Down
Loading

0 comments on commit 851cb7b

Please sign in to comment.