diff --git a/README.md b/README.md index 2a24d3e..9e3af26 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ h | Open help dialog s | Select container sort field r | Reverse container sort order m | Manage container (start, stop and/or remove) -l | View container logs +l | View container logs (`t` to toggle timestamp when open) q | Quit ctop [build]: _docs/build.md diff --git a/cwidgets/single/main.go b/cwidgets/single/main.go index 27f35e6..6f963c1 100644 --- a/cwidgets/single/main.go +++ b/cwidgets/single/main.go @@ -91,9 +91,6 @@ func (e *Single) Align() { log.Debugf("align: width=%v left-col=%v right-col=%v", e.Width, colWidth[0], colWidth[1]) } -func calcWidth(w int) { -} - func (e *Single) Buffer() ui.Buffer { buf := ui.NewBuffer() if e.Width < (colWidth[0] + colWidth[1]) { diff --git a/menus.go b/menus.go index cec0548..046eba7 100644 --- a/menus.go +++ b/menus.go @@ -6,16 +6,20 @@ import ( "github.com/bcicen/ctop/widgets" "github.com/bcicen/ctop/widgets/menu" ui "github.com/gizak/termui" + "time" + "fmt" ) var helpDialog = []menu.Item{ - menu.Item{"[a] - toggle display of all containers", ""}, - menu.Item{"[f] - filter displayed containers", ""}, - menu.Item{"[h] - open this help dialog", ""}, - menu.Item{"[H] - toggle ctop header", ""}, - menu.Item{"[s] - select container sort field", ""}, - menu.Item{"[r] - reverse container sort order", ""}, - menu.Item{"[q] - exit ctop", ""}, + {"[a] - toggle display of all containers", ""}, + {"[f] - filter displayed containers", ""}, + {"[h] - open this help dialog", ""}, + {"[H] - toggle ctop header", ""}, + {"[s] - select container sort field", ""}, + {"[r] - reverse container sort order", ""}, + {"[m] - Manage container (start, stop and/or remove)", ""}, + {"[l] - View container logs ([t] to toggle timestamp when open)", ""}, + {"[q] - exit ctop", ""}, } func HelpMenu() { @@ -162,7 +166,9 @@ func LogMenu() { ui.Handle("/sys/wnd/resize", func(e ui.Event) { m.Resize() - ui.Render(m) + }) + ui.Handle("/sys/kbd/t", func(ui.Event) { + m.Toggle() }) ui.Handle("/sys/kbd/", func(ui.Event) { quit <- true @@ -171,18 +177,30 @@ func LogMenu() { ui.Loop() } -func logReader(container *container.Container) (logs chan string, quit chan bool) { +type toggleLog struct { + timestamp time.Time + message string +} + +func (t *toggleLog) Toggle(on bool) string { + if on { + return fmt.Sprintf("%s %s", t.timestamp.Format("2006-01-02T15:04:05.999Z07:00"), t.message) + } + return t.message +} + +func logReader(container *container.Container) (logs chan widgets.ToggleText, quit chan bool) { logCollector := container.Logs() stream := logCollector.Stream() - logs = make(chan string) + logs = make(chan widgets.ToggleText) quit = make(chan bool) go func() { for { select { case log := <-stream: - logs <- log.Message + logs <- &toggleLog{timestamp: log.Timestamp, message: log.Message} case <-quit: logCollector.Stop() close(logs) diff --git a/widgets/view.go b/widgets/view.go index 4681e68..1347de1 100644 --- a/widgets/view.go +++ b/widgets/view.go @@ -4,23 +4,29 @@ import ( ui "github.com/gizak/termui" ) +type ToggleText interface { + // returns text for toggle on/off + Toggle(on bool) string +} + type TextView struct { ui.Block - inputStream <-chan string + inputStream <-chan ToggleText render chan bool - Text []string // all the text - TextOut []string // text to be displayed + toggleState bool + Text []ToggleText // all the text + TextOut []string // text to be displayed TextFgColor ui.Attribute TextBgColor ui.Attribute padding Padding } -func NewTextView(lines <-chan string) *TextView { +func NewTextView(lines <-chan ToggleText) *TextView { t := &TextView{ Block: *ui.NewBlock(), inputStream: lines, render: make(chan bool), - Text: []string{}, + Text: []ToggleText{}, TextOut: []string{}, TextFgColor: ui.ThemeAttr("menu.text.fg"), TextBgColor: ui.ThemeAttr("menu.text.bg"), @@ -37,6 +43,8 @@ func NewTextView(lines <-chan string) *TextView { return t } +// Adjusts text inside this view according to the window size. No need to call ui.Render(...) +// after calling this method, it is called automatically func (t *TextView) Resize() { ui.Clear() t.Height = ui.TermHeight() @@ -44,6 +52,13 @@ func (t *TextView) Resize() { t.render <- true } +// Toggles text inside this view. No need to call ui.Render(...) after calling this method, +// it is called automatically +func (t *TextView) Toggle() { + t.toggleState = !t.toggleState + t.render <- true +} + func (t *TextView) Buffer() ui.Buffer { var cell ui.Cell buf := t.Block.Buffer() @@ -70,7 +85,7 @@ func (t *TextView) renderLoop() { height := t.Height - (t.padding[1] * 2) t.TextOut = []string{} for i := len(t.Text) - 1; i >= 0; i-- { - lines := splitLine(t.Text[i], maxWidth) + lines := splitLine(t.Text[i].Toggle(t.toggleState), maxWidth) t.TextOut = append(lines, t.TextOut...) if len(t.TextOut) > height { t.TextOut = t.TextOut[:height]