diff --git a/cmd/gosdn-tview/app/app.go b/cmd/gosdn-tview/app/app.go index 28a8bc9ed84aedf5c962f88d9035c4cbb2b43792..037c1adc771ed4a092241a9930b5bb0ba30e4279 100644 --- a/cmd/gosdn-tview/app/app.go +++ b/cmd/gosdn-tview/app/app.go @@ -1,6 +1,8 @@ package app -import "github.com/rivo/tview" +import ( + "github.com/rivo/tview" +) type view interface { GetContent() tview.Primitive @@ -17,6 +19,7 @@ func NewApp() *App { a := &App{ app: tview.NewApplication(), } + return a } diff --git a/cmd/gosdn-tview/grpc/commands.go b/cmd/gosdn-tview/grpc/commands.go index 1ce55dd37b4b70e2cce9fa5aa0b4a1f24d42d9a8..5b0c0790601164401d30bd6a39d86377627e355a 100644 --- a/cmd/gosdn-tview/grpc/commands.go +++ b/cmd/gosdn-tview/grpc/commands.go @@ -35,6 +35,7 @@ func Connect(address string) (*grpc.ClientConn, error) { return grpc.Dial(address, grpc.WithInsecure(), grpc.WithTimeout(5*time.Second), grpc.WithBlock()) } +//GoSDNLogStream creates a continuous gRPC stream to recieve goSDN logs func GoSDNLogStream(conn *grpc.ClientConn, clv *tview.TextView) error { var streamError error c := pb.NewGrpcCliClient(conn) diff --git a/cmd/gosdn-tview/views/addPNDView.go b/cmd/gosdn-tview/views/addPNDView.go index 14fcd475d3b8eb9d13ea4524dc17a00c081cf67f..65a9adb6ee0c5f668104432e3b278dd4c77aed9a 100644 --- a/cmd/gosdn-tview/views/addPNDView.go +++ b/cmd/gosdn-tview/views/addPNDView.go @@ -7,17 +7,19 @@ import ( //AddPNDView is an application view to create a new goSDN PND type AddPNDView struct { + title string addPNDView *tview.Form } //NewAddPNDView creates a new AddPNDView -func NewAddPNDView(app *app.App) *AddPNDView { +func NewAddPNDView(title string, app *app.App) *AddPNDView { pndv := &AddPNDView{ + title: title, addPNDView: tview.NewForm(), } pndv.addPNDView. SetBorder(true). - SetTitle("Add new PND") + SetTitle(pndv.title) pndv.addPNDView. AddInputField("Name", "", 20, nil, nil). @@ -40,3 +42,8 @@ func NewAddPNDView(app *app.App) *AddPNDView { func (pndv *AddPNDView) GetContent() tview.Primitive { return pndv.addPNDView } + +//GetTitle returns the title of the specific view +func (pndv *AddPNDView) GetTitle() string { + return pndv.title +} diff --git a/cmd/gosdn-tview/views/commandsListView.go b/cmd/gosdn-tview/views/commandsListView.go index b9e42b9049d960c097ec2a383d5df0f947641a66..745fd4523a568c081cf66b37c078ae831d2ec3ca 100644 --- a/cmd/gosdn-tview/views/commandsListView.go +++ b/cmd/gosdn-tview/views/commandsListView.go @@ -9,17 +9,19 @@ import ( //CommandListView is an application view to display all the goSDN commands type CommandListView struct { + title string commandsList *tview.List } //NewCommandListView creates a new CommandListView -func NewCommandListView() *CommandListView { +func NewCommandListView(title string) *CommandListView { cv := &CommandListView{ + title: title, commandsList: tview.NewList(), } cv.commandsList. SetBorder(true). - SetTitle("Commands") + SetTitle(cv.title) return cv } @@ -29,6 +31,11 @@ func (cv *CommandListView) GetContent() tview.Primitive { return cv.commandsList } +//GetTitle returns the title of the specific view +func (cv *CommandListView) GetTitle() string { + return cv.title +} + //GetCommands gets all goSDN commands from a command list and creates new //tview.List items for each one of them. The specific gRPC functions are added //as tview.Selected() function @@ -36,7 +43,7 @@ func (cv *CommandListView) GetCommands(app *app.App, rv *ResultAndInputView, conn *grpc.ClientConn) { //TODO: create own command in grpc -> commands cv.commandsList.AddItem("addPND", "closes the application", 'a', func() { - rv.ChangeContentView("addPND") + rv.ChangeContentView(rv.pndInputView.GetTitle()) app.SetFocus(rv.GetContent()) }) @@ -46,12 +53,14 @@ func (cv *CommandListView) GetCommands(app *app.App, rv *ResultAndInputView, AddItem(command.Name, command.Description, rune('b'+i), func() { r := f(conn) rv.SetContent(r) - rv.ChangeContentView("result") + rv.ChangeContentView(rv.GetTitle()) + app.SetFocus(rv.GetContent()) }) } cv.commandsList.AddItem("log", "shows the log of goSDN", 'l', func() { - rv.ChangeContentView("log") + rv.ChangeContentView(rv.consoleLogView.GetTitle()) + app.SetFocus(rv.GetContent()) }) cv.commandsList.AddItem("quit", "closes the application", 'q', func() { diff --git a/cmd/gosdn-tview/views/consoleLogView.go b/cmd/gosdn-tview/views/consoleLogView.go index 55de041dfaeb065382b356f83385cfc745ced67b..0c7769d2ae61d6e4fb1a5dc15fce4832fd025e95 100644 --- a/cmd/gosdn-tview/views/consoleLogView.go +++ b/cmd/gosdn-tview/views/consoleLogView.go @@ -8,20 +8,23 @@ import ( //ConsoleLogView is an application view to create a view for goSDN log messages type ConsoleLogView struct { + title string consoleLogView *tview.TextView } //NewConsoleLogView creates a new ConsoleLogView -func NewConsoleLogView(conn *grpc.ClientConn) *ConsoleLogView { +func NewConsoleLogView(title string, conn *grpc.ClientConn) *ConsoleLogView { clv := &ConsoleLogView{ consoleLogView: tview.NewTextView(), + title: title, } clv.consoleLogView. SetDynamicColors(true). - SetTextAlign(tview.AlignCenter). + SetTextAlign(tview.AlignLeft). + SetScrollable(true). SetRegions(true). SetBorder(true). - SetTitle("goSDN Logs") + SetTitle(clv.title) commands.GoSDNLogStream(conn, clv.consoleLogView) @@ -40,3 +43,8 @@ func (clv *ConsoleLogView) Write(s string) (n int, err error) { b := []byte(s) return clv.consoleLogView.Write(b) } + +//GetTitle returns the title of the specific view +func (clv *ConsoleLogView) GetTitle() string { + return clv.title +} diff --git a/cmd/gosdn-tview/views/footerView.go b/cmd/gosdn-tview/views/footerView.go index 09ff7d7dd0771036655b5291f2a5c278dc6ce72a..b79ce1054a104b24040860d7fa20eef1da42519e 100644 --- a/cmd/gosdn-tview/views/footerView.go +++ b/cmd/gosdn-tview/views/footerView.go @@ -22,9 +22,6 @@ func NewFooterView(app *app.App, conn *grpc.ClientConn) *FooterView { databaseStatusView: NewDatabaseStatusView(app), gRPCStatusView: NewGRPCStatusView(app, conn), } - fw.footerView. - SetBorder(true). - SetTitle("Status") fw.footerView. AddItem(fw.gRPCStatusView.GetContent(), 0, 1, false). diff --git a/cmd/gosdn-tview/views/mainView.go b/cmd/gosdn-tview/views/mainView.go index e14bef6dc1d4997d0e7327f276956a6bb6f70bc6..22aa23ed911316da10e7a14fd89c199e81513730 100644 --- a/cmd/gosdn-tview/views/mainView.go +++ b/cmd/gosdn-tview/views/mainView.go @@ -21,13 +21,13 @@ type MainView struct { //NewMainView creates a new MainView func NewMainView(app *app.App, conn *grpc.ClientConn) *MainView { mv := &MainView{ - pages: tview.NewPages(), - mainGrid: tview.NewGrid(), - commandsListView: NewCommandListView(), - resultAndInputView: NewResultAndInputView(app, conn), - headerView: NewHeaderView(), - footerView: NewFooterView(app, conn), + pages: tview.NewPages(), + mainGrid: tview.NewGrid(), + commandsListView: NewCommandListView("commands"), + headerView: NewHeaderView(), + footerView: NewFooterView(app, conn), } + mv.resultAndInputView = NewResultAndInputView("result and input", app, mv.commandsListView.GetContent(), conn) mv.commandsListView.GetCommands(app, mv.resultAndInputView, conn) mv.mainGrid. @@ -36,7 +36,8 @@ func NewMainView(app *app.App, conn *grpc.ClientConn) *MainView { AddItem(mv.headerView.GetContent(), 0, 0, 1, 2, 0, 0, false). AddItem(mv.footerView.GetContent(), 2, 0, 1, 2, 0, 0, false) - mv.mainGrid.AddItem(mv.commandsListView.GetContent(), 1, 0, 1, 1, 0, 0, true). + mv.mainGrid. + AddItem(mv.commandsListView.GetContent(), 1, 0, 1, 1, 0, 0, true). AddItem(mv.resultAndInputView.GetContent(), 1, 1, 1, 1, 0, 0, false) mv.pages.AddPage("main", mv.mainGrid, true, true) diff --git a/cmd/gosdn-tview/views/resultAndInputView.go b/cmd/gosdn-tview/views/resultAndInputView.go index 67f83f2a3ea0b9a9444eb71a225209aa63ed667a..ff012a0f6e045afbc40708b96e5a04d3373c336d 100644 --- a/cmd/gosdn-tview/views/resultAndInputView.go +++ b/cmd/gosdn-tview/views/resultAndInputView.go @@ -2,6 +2,7 @@ package views import ( "code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app" + "github.com/gdamore/tcell/v2" "github.com/rivo/tview" "google.golang.org/grpc" ) @@ -9,6 +10,7 @@ import ( //ResultAndInputView is an application view to display different other views. //Depending on the required features the views are changed. type ResultAndInputView struct { + title string pages *tview.Pages resultView *tview.TextView pndInputView *AddPNDView @@ -16,24 +18,33 @@ type ResultAndInputView struct { } //NewResultAndInputView creates a new ResultAndInputView -func NewResultAndInputView(app *app.App, conn *grpc.ClientConn) *ResultAndInputView { +func NewResultAndInputView(title string, app *app.App, commandListView tview.Primitive, conn *grpc.ClientConn) *ResultAndInputView { rv := &ResultAndInputView{ + title: title, pages: tview.NewPages(), - pndInputView: NewAddPNDView(app), + pndInputView: NewAddPNDView("add PND", app), resultView: tview.NewTextView(), - consoleLogView: NewConsoleLogView(conn), + consoleLogView: NewConsoleLogView("logs", conn), } rv.resultView. SetDynamicColors(true). SetRegions(true). SetScrollable(true). - SetTitle("Result"). + SetTitle(rv.title). SetBorder(true) rv.pages. - AddPage("result", rv.resultView, true, true). - AddPage("addPND", rv.pndInputView.GetContent(), true, false). - AddPage("log", rv.consoleLogView.GetContent(), true, false) + AddPage(rv.title, rv.resultView, true, true). + AddPage(rv.pndInputView.GetTitle(), rv.pndInputView.GetContent(), true, false). + AddPage(rv.consoleLogView.GetTitle(), rv.consoleLogView.GetContent(), true, false) + + rv.pages.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + k := event.Key() + if k == tcell.KeyESC { + app.SetFocus(commandListView) + } + return event + }) return rv } @@ -54,3 +65,8 @@ func (rv *ResultAndInputView) SetContent(s string) { rv.resultView.Clear() rv.resultView.SetText(s) } + +//GetTitle returns the title of the specific view +func (rv *ResultAndInputView) GetTitle() string { + return rv.title +} diff --git a/go.mod b/go.mod index b55c84e7746a120ddb95e8d00d1776078cc7b6dc..7411edc9a0644737f93b37777e32816755f822e0 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( code.fbi.h-da.de/cocsn/swagger/apis v0.0.0-20200924152423-61030cab7b88 github.com/BurntSushi/toml v0.3.1 + github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591 github.com/go-openapi/runtime v0.19.22 github.com/go-openapi/strfmt v0.19.5 github.com/golang/protobuf v1.4.2 diff --git a/nucleus/cli-handling.go b/nucleus/cli-handling.go index 2afb6f6f3a87e1597b5179e16d813c0cad92ec7d..f4e41b78f31ebf950e1b1b8a84c2938038dbcbe7 100644 --- a/nucleus/cli-handling.go +++ b/nucleus/cli-handling.go @@ -20,7 +20,7 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -type LogConnection struct { +type logConnection struct { stream pb.GrpcCli_CreateLogStreamServer id string active bool @@ -31,7 +31,7 @@ type LogConnection struct { type server struct { pb.UnimplementedGrpcCliServer core *Core - logConnections []*LogConnection + logConnections []*logConnection } var srv *server @@ -52,7 +52,7 @@ func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloRe //GetLog creates a continuous stream between client and server to send goSDN logs func (s *server) CreateLogStream(req *emptypb.Empty, stream pb.GrpcCli_CreateLogStreamServer) error { - conn := &LogConnection{ + conn := &logConnection{ stream: stream, active: true, error: make(chan error), @@ -69,7 +69,7 @@ func (s *server) BroadcastLog(log *pb.LogReply) { for _, conn := range s.logConnections { wait.Add(1) - go func(conn *LogConnection) { + go func(conn *logConnection) { defer wait.Done() if conn.active { err := conn.stream.Send(log) @@ -99,7 +99,7 @@ func (s *server) Shutdown(ctx context.Context, in *pb.ShutdownRequest) (*pb.Shut func getCLIGoing(core *Core) { - var logConnections []*LogConnection + var logConnections []*logConnection var logBuffer buf log.Info("Starting: GetCLIGoing")