From 464207797581d95d2537890932a4bd18e4a6a8bf Mon Sep 17 00:00:00 2001 From: Alexandru Placinta Date: Sun, 12 Jan 2025 18:27:49 +0200 Subject: [PATCH] Dialog boxes use dialog style (#2150) --- internal/ui/dialog/confirm.go | 9 +-- internal/view/context.go | 46 ++++++++------- internal/view/cronjob.go | 100 ++++++++++---------------------- internal/view/drain_dialog.go | 17 +++--- internal/view/image_extender.go | 68 ++++++++++++---------- internal/view/pf.go | 41 +++---------- internal/view/rs.go | 27 ++------- internal/view/scale_extender.go | 33 +++++------ 8 files changed, 134 insertions(+), 207 deletions(-) diff --git a/internal/ui/dialog/confirm.go b/internal/ui/dialog/confirm.go index 461456cb..c2314376 100644 --- a/internal/ui/dialog/confirm.go +++ b/internal/ui/dialog/confirm.go @@ -68,13 +68,14 @@ func ShowConfirmAck(app *ui.App, pages *ui.Pages, acceptStr string, override boo // ShowConfirm pops a confirmation dialog. func ShowConfirm(styles config.Dialog, pages *ui.Pages, title, msg string, ack confirmFunc, cancel cancelFunc) { - f := tview.NewForm() - f.SetItemPadding(0) - f.SetButtonsAlign(tview.AlignCenter). + f := tview.NewForm(). + SetItemPadding(0). + SetButtonsAlign(tview.AlignCenter). SetButtonBackgroundColor(styles.ButtonBgColor.Color()). SetButtonTextColor(styles.ButtonFgColor.Color()). SetLabelColor(styles.LabelFgColor.Color()). - SetFieldTextColor(styles.FieldFgColor.Color()) + SetFieldTextColor(styles.FieldFgColor.Color()). + SetFieldBackgroundColor(styles.BgColor.Color()) f.AddButton("Cancel", func() { dismiss(pages) cancel() diff --git a/internal/view/context.go b/internal/view/context.go index 4ba51bf9..e4788229 100644 --- a/internal/view/context.go +++ b/internal/view/context.go @@ -48,8 +48,7 @@ func (c *Context) renameCmd(evt *tcell.EventKey) *tcell.EventKey { return evt } - app := c.App() - c.showRenameModal(app, contextName, c.renameDialogCallback) + c.showRenameModal(contextName, c.renameDialogCallback) return nil } @@ -65,39 +64,42 @@ func (c *Context) renameDialogCallback(form *tview.Form, contextName string) err return nil } -func (c *Context) showRenameModal(a *App, name string, ok func(form *tview.Form, contextName string) error) { - p := a.Content.Pages - f := c.makeStyledForm() +func (c *Context) showRenameModal(name string, ok func(form *tview.Form, contextName string) error) { + app := c.App() + styles := app.Styles.Dialog() + + f := tview.NewForm(). + SetItemPadding(0). + SetButtonsAlign(tview.AlignCenter). + SetButtonBackgroundColor(styles.ButtonBgColor.Color()). + SetButtonTextColor(styles.ButtonFgColor.Color()). + SetLabelColor(styles.LabelFgColor.Color()). + SetFieldTextColor(styles.FieldFgColor.Color()) f.AddInputField(inputField, name, 0, nil, nil). AddButton("OK", func() { if err := ok(f, name); err != nil { - c.App().Flash().Err(err) + app.Flash().Err(err) return } - p.RemovePage(renamePage) + app.Content.Pages.RemovePage(renamePage) }). AddButton("Cancel", func() { - p.RemovePage(renamePage) + app.Content.Pages.RemovePage(renamePage) }) + m := tview.NewModalForm("", f) m.SetText(fmt.Sprintf("Rename context %q?", name)) m.SetDoneFunc(func(int, string) { - p.RemovePage(renamePage) + app.Content.Pages.RemovePage(renamePage) }) - p.AddPage(renamePage, m, false, false) - p.ShowPage(renamePage) -} + app.Content.Pages.AddPage(renamePage, m, false, false) + app.Content.Pages.ShowPage(renamePage) -func (c *Context) makeStyledForm() *tview.Form { - f := tview.NewForm() - f.SetItemPadding(0) - f.SetButtonsAlign(tview.AlignCenter). - SetButtonBackgroundColor(tview.Styles.PrimitiveBackgroundColor). - SetButtonTextColor(tview.Styles.PrimaryTextColor). - SetLabelColor(tcell.ColorAqua). - SetFieldTextColor(tcell.ColorOrange) - - return f + for i := 0; i < f.GetButtonCount(); i++ { + f.GetButton(i). + SetBackgroundColorActivated(styles.ButtonFocusBgColor.Color()). + SetLabelColorActivated(styles.ButtonFocusFgColor.Color()) + } } func (c *Context) useCtx(app *App, model ui.Tabular, gvr client.GVR, path string) { diff --git a/internal/view/cronjob.go b/internal/view/cronjob.go index 5b1411cf..fb413ac6 100644 --- a/internal/view/cronjob.go +++ b/internal/view/cronjob.go @@ -111,90 +111,54 @@ func (c *CronJob) triggerCmd(evt *tcell.EventKey) *tcell.EventKey { } func (c *CronJob) toggleSuspendCmd(evt *tcell.EventKey) *tcell.EventKey { - sel := c.GetTable().GetSelectedItem() + table := c.GetTable() + sel := table.GetSelectedItem() + if sel == "" { return evt } + cell := table.GetCell(c.GetTable().GetSelectedRowIndex(), c.GetTable().NameColIndex()+2) + + if cell == nil { + c.App().Flash().Errf("Unable to assert current status") + return nil + } + c.Stop() defer c.Start() - c.showSuspendDialog(sel) + + c.showSuspendDialog(cell, sel) return nil } -func (c *CronJob) showSuspendDialog(sel string) { - cell := c.GetTable().GetCell(c.GetTable().GetSelectedRowIndex(), c.GetTable().NameColIndex()+2) - if cell == nil { - c.App().Flash().Errf("Unable to assert current status") - return - } - suspended := strings.TrimSpace(cell.Text) == defaultSuspendStatus +func (c *CronJob) showSuspendDialog(cell *tview.TableCell, sel string) { title := "Suspend" - if suspended { + + if strings.TrimSpace(cell.Text) == defaultSuspendStatus { title = "Resume" } - confirm := tview.NewModalForm(fmt.Sprintf("<%s>", title), c.makeSuspendForm(sel, !suspended)) - confirm.SetText(fmt.Sprintf("%s CronJob %s?", title, sel)) - confirm.SetDoneFunc(func(int, string) { - c.dismissDialog() - }) - c.App().Content.AddPage(suspendDialogKey, confirm, false, false) - c.App().Content.ShowPage(suspendDialogKey) -} - -func (c *CronJob) makeSuspendForm(sel string, suspend bool) *tview.Form { - f := c.makeStyledForm() - action := "suspended" - if !suspend { - action = "resumed" - } - - f.AddButton("Cancel", func() { - c.dismissDialog() - }) - f.AddButton("OK", func() { - defer c.dismissDialog() - + dialog.ShowConfirm(c.App().Styles.Dialog(), c.App().Content.Pages, title, sel, func() { ctx, cancel := context.WithTimeout(context.Background(), c.App().Conn().Config().CallTimeout()) defer cancel() - if err := c.toggleSuspend(ctx, sel); err != nil { - log.Error().Err(err).Msgf("CronJob %s %s failed", sel, action) - c.App().Flash().Err(err) - } else { - c.App().Flash().Infof("CronJob %s %s successfully!", sel, action) + + res, err := dao.AccessorFor(c.App().factory, c.GVR()) + if err != nil { + c.App().Flash().Err(fmt.Errorf("no accessor for %q", c.GVR())) + return } - }) - return f -} - -func (c *CronJob) toggleSuspend(ctx context.Context, path string) error { - res, err := dao.AccessorFor(c.App().factory, c.GVR()) - if err != nil { - return err - } - cronJob, ok := res.(*dao.CronJob) - if !ok { - return fmt.Errorf("expecting a scalable resource for %q", c.GVR()) - } - - return cronJob.ToggleSuspend(ctx, path) -} - -func (c *CronJob) makeStyledForm() *tview.Form { - f := tview.NewForm() - f.SetItemPadding(0) - f.SetButtonsAlign(tview.AlignCenter). - SetButtonBackgroundColor(tview.Styles.PrimitiveBackgroundColor). - SetButtonTextColor(tview.Styles.PrimaryTextColor). - SetLabelColor(tcell.ColorAqua). - SetFieldTextColor(tcell.ColorOrange) - - return f -} - -func (c *CronJob) dismissDialog() { - c.App().Content.RemovePage(suspendDialogKey) + cronJob, ok := res.(*dao.CronJob) + if !ok { + c.App().Flash().Errf("expecting a cron job for %q", c.GVR()) + return + } + + if err := cronJob.ToggleSuspend(ctx, sel); err != nil { + c.App().Flash().Errf("Cronjob %s failed for %v", strings.ToLower(title), err) + return + } + }, func() {}) } diff --git a/internal/view/drain_dialog.go b/internal/view/drain_dialog.go index ad3ba51d..d6b71bb9 100644 --- a/internal/view/drain_dialog.go +++ b/internal/view/drain_dialog.go @@ -20,15 +20,16 @@ type DrainFunc func(v ResourceViewer, sels []string, opts dao.DrainOptions) // ShowDrain pops a node drain dialog. func ShowDrain(view ResourceViewer, sels []string, opts dao.DrainOptions, okFn DrainFunc) { - styles := view.App().Styles + styles := view.App().Styles.Dialog() - f := tview.NewForm() - f.SetItemPadding(0) - f.SetButtonsAlign(tview.AlignCenter). - SetButtonBackgroundColor(styles.BgColor()). - SetButtonTextColor(styles.FgColor()). - SetLabelColor(styles.K9s.Info.FgColor.Color()). - SetFieldTextColor(styles.K9s.Info.SectionColor.Color()) + f := tview.NewForm(). + SetItemPadding(0). + SetButtonsAlign(tview.AlignCenter). + SetButtonBackgroundColor(styles.ButtonBgColor.Color()). + SetButtonTextColor(styles.ButtonFgColor.Color()). + SetLabelColor(styles.LabelFgColor.Color()). + SetFieldTextColor(styles.FieldFgColor.Color()). + SetFieldBackgroundColor(styles.BgColor.Color()) f.AddInputField("GracePeriod:", strconv.Itoa(opts.GracePeriodSeconds), 0, nil, func(v string) { a, err := asIntOpt(v) diff --git a/internal/view/image_extender.go b/internal/view/image_extender.go index bc8f2a7b..10d99dda 100644 --- a/internal/view/image_extender.go +++ b/internal/view/image_extender.go @@ -95,11 +95,11 @@ func (s *ImageExtender) showImageDialog(path string) error { } func (s *ImageExtender) makeSetImageForm(sel string) (*tview.Form, error) { - f := s.makeStyledForm() podSpec, err := s.getPodSpec(sel) if err != nil { return nil, err } + formContainerLines := make([]*imageFormSpec, 0, len(podSpec.InitContainers)+len(podSpec.Containers)) for _, spec := range podSpec.InitContainers { formContainerLines = append(formContainerLines, &imageFormSpec{init: true, name: spec.Name, dockerImage: spec.Image}) @@ -107,6 +107,36 @@ func (s *ImageExtender) makeSetImageForm(sel string) (*tview.Form, error) { for _, spec := range podSpec.Containers { formContainerLines = append(formContainerLines, &imageFormSpec{name: spec.Name, dockerImage: spec.Image}) } + + styles := s.App().Styles.Dialog() + f := tview.NewForm(). + SetItemPadding(0). + SetButtonsAlign(tview.AlignCenter). + SetButtonBackgroundColor(styles.ButtonBgColor.Color()). + SetButtonTextColor(styles.ButtonFgColor.Color()). + SetLabelColor(styles.LabelFgColor.Color()). + SetFieldTextColor(styles.FieldFgColor.Color()). + AddButton("OK", func() { + defer s.dismissDialog() + var imageSpecsModified dao.ImageSpecs + for _, v := range formContainerLines { + if v.modified() { + imageSpecsModified = append(imageSpecsModified, v.imageSpec()) + } + } + ctx, cancel := context.WithTimeout(context.Background(), s.App().Conn().Config().CallTimeout()) + defer cancel() + if err := s.setImages(ctx, sel, imageSpecsModified); err != nil { + log.Error().Err(err).Msgf("PodSpec %s image update failed", sel) + s.App().Flash().Err(err) + return + } + s.App().Flash().Infof("Resource %s:%s image updated successfully", s.GVR(), sel) + }). + AddButton("Cancel", func() { + s.dismissDialog() + }) + for i := range formContainerLines { ctn := formContainerLines[i] f.AddInputField(ctn.name, ctn.dockerImage, 0, nil, func(changed string) { @@ -114,26 +144,11 @@ func (s *ImageExtender) makeSetImageForm(sel string) (*tview.Form, error) { }) } - f.AddButton("OK", func() { - defer s.dismissDialog() - var imageSpecsModified dao.ImageSpecs - for _, v := range formContainerLines { - if v.modified() { - imageSpecsModified = append(imageSpecsModified, v.imageSpec()) - } - } - ctx, cancel := context.WithTimeout(context.Background(), s.App().Conn().Config().CallTimeout()) - defer cancel() - if err := s.setImages(ctx, sel, imageSpecsModified); err != nil { - log.Error().Err(err).Msgf("PodSpec %s image update failed", sel) - s.App().Flash().Err(err) - return - } - s.App().Flash().Infof("Resource %s:%s image updated successfully", s.GVR(), sel) - }) - f.AddButton("Cancel", func() { - s.dismissDialog() - }) + for i := 0; i < f.GetButtonCount(); i++ { + f.GetButton(i). + SetBackgroundColorActivated(styles.ButtonFocusBgColor.Color()). + SetLabelColorActivated(styles.ButtonFocusFgColor.Color()) + } return f, nil } @@ -142,17 +157,6 @@ func (s *ImageExtender) dismissDialog() { s.App().Content.RemovePage(imageKey) } -func (s *ImageExtender) makeStyledForm() *tview.Form { - f := tview.NewForm() - f.SetItemPadding(0) - f.SetButtonsAlign(tview.AlignCenter). - SetButtonBackgroundColor(tview.Styles.PrimitiveBackgroundColor). - SetButtonTextColor(tview.Styles.PrimaryTextColor). - SetLabelColor(tcell.ColorAqua). - SetFieldTextColor(tcell.ColorOrange) - return f -} - func (s *ImageExtender) getPodSpec(path string) (*corev1.PodSpec, error) { res, err := dao.AccessorFor(s.App().factory, s.GVR()) if err != nil { diff --git a/internal/view/pf.go b/internal/view/pf.go index a92a1824..246bd9ed 100644 --- a/internal/view/pf.go +++ b/internal/view/pf.go @@ -15,8 +15,8 @@ import ( "github.com/derailed/k9s/internal/model" "github.com/derailed/k9s/internal/perf" "github.com/derailed/k9s/internal/ui" + "github.com/derailed/k9s/internal/ui/dialog" "github.com/derailed/tcell/v2" - "github.com/derailed/tview" "github.com/rs/zerolog/log" ) @@ -165,16 +165,14 @@ func (p *PortForward) deleteCmd(evt *tcell.EventKey) *tcell.EventKey { var msg string if len(selections) > 1 { msg = fmt.Sprintf("Delete %d marked %s?", len(selections), p.GVR()) + } else if h, err := pfToHuman(selections[0]); err == nil { + msg = fmt.Sprintf("Delete %s %s?", p.GVR().R(), h) } else { - h, err := pfToHuman(selections[0]) - if err == nil { - msg = fmt.Sprintf("Delete %s %s?", p.GVR().R(), h) - } else { - p.App().Flash().Err(err) - return nil - } + p.App().Flash().Err(err) + return nil } - showModal(p.App(), msg, func() { + + dialog.ShowConfirm(p.App().Styles.Dialog(), p.App().Content.Pages, "Delete", msg, func() { for _, s := range selections { var pf dao.PortForward pf.Init(p.App().factory, client.NewGVR("portforwards")) @@ -185,7 +183,7 @@ func (p *PortForward) deleteCmd(evt *tcell.EventKey) *tcell.EventKey { } p.App().Flash().Infof("Successfully deleted %d PortForward!", len(selections)) p.GetTable().Refresh() - }) + }, func() {}) return nil } @@ -203,26 +201,3 @@ func pfToHuman(s string) (string, error) { return fmt.Sprintf("%s::%s %s->%s", mm[2], mm[3], mm[4], mm[5]), nil } - -func showModal(a *App, msg string, ok func()) { - p := a.Content.Pages - styles := a.Styles.Dialog() - m := tview.NewModal(). - AddButtons([]string{"Cancel", "OK"}). - SetButtonBackgroundColor(styles.ButtonBgColor.Color()). - SetTextColor(tcell.ColorFuchsia). - SetText(msg). - SetDoneFunc(func(_ int, b string) { - if b == "OK" { - ok() - } - dismissModal(p) - }) - m.SetTitle("") - p.AddPage(promptPage, m, false, false) - p.ShowPage(promptPage) -} - -func dismissModal(p *ui.Pages) { - p.RemovePage(promptPage) -} diff --git a/internal/view/rs.go b/internal/view/rs.go index 07178563..1c552478 100644 --- a/internal/view/rs.go +++ b/internal/view/rs.go @@ -9,8 +9,8 @@ import ( "github.com/derailed/k9s/internal/client" "github.com/derailed/k9s/internal/dao" "github.com/derailed/k9s/internal/ui" + "github.com/derailed/k9s/internal/ui/dialog" "github.com/derailed/tcell/v2" - "github.com/derailed/tview" ) // ReplicaSet presents a replicaset viewer. @@ -59,12 +59,9 @@ func (r *ReplicaSet) rollbackCmd(evt *tcell.EventKey) *tcell.EventKey { return evt } - r.showModal(fmt.Sprintf("Rollback %s %s?", r.GVR(), path), func(_ int, button string) { - defer r.dismissModal() + msg := fmt.Sprintf("Rollback %s %s?", r.GVR(), path) - if button != "OK" { - return - } + dialog.ShowConfirm(r.App().Styles.Dialog(), r.App().Content.Pages, "Rollback", msg, func() { r.App().Flash().Infof("Rolling back %s %s", r.GVR(), path) var drs dao.ReplicaSet drs.Init(r.App().factory, r.GVR()) @@ -74,23 +71,7 @@ func (r *ReplicaSet) rollbackCmd(evt *tcell.EventKey) *tcell.EventKey { r.App().Flash().Infof("%s successfully rolled back", path) } r.Refresh() - }) + }, func() {}) return nil } - -func (r *ReplicaSet) dismissModal() { - r.App().Content.RemovePage("confirm") -} - -func (r *ReplicaSet) showModal(msg string, done func(int, string)) { - styles := r.App().Styles.Dialog() - confirm := tview.NewModal(). - AddButtons([]string{"Cancel", "OK"}). - SetButtonBackgroundColor(styles.ButtonBgColor.Color()). - SetTextColor(tcell.ColorFuchsia). - SetText(msg). - SetDoneFunc(done) - r.App().Content.AddPage("confirm", confirm, false, false) - r.App().Content.ShowPage("confirm") -} diff --git a/internal/view/scale_extender.go b/internal/view/scale_extender.go index e402bd5a..e10666fa 100644 --- a/internal/view/scale_extender.go +++ b/internal/view/scale_extender.go @@ -9,8 +9,6 @@ import ( "strconv" "strings" - "github.com/derailed/k9s/internal/config" - "github.com/derailed/k9s/internal/dao" "github.com/derailed/k9s/internal/ui" "github.com/derailed/tcell/v2" @@ -84,9 +82,6 @@ func (s *ScaleExtender) valueOf(col string) (string, error) { } func (s *ScaleExtender) makeScaleForm(sels []string) (*tview.Form, error) { - styles := s.App().Styles.Dialog() - f := s.makeStyledForm(styles) - factor := "0" if len(sels) == 1 { replicas, err := s.valueOf("READY") @@ -99,6 +94,16 @@ func (s *ScaleExtender) makeScaleForm(sels []string) (*tview.Form, error) { } factor = strings.TrimRight(tokens[1], ui.DeltaSign) } + + styles := s.App().Styles.Dialog() + f := tview.NewForm(). + SetItemPadding(0). + SetButtonsAlign(tview.AlignCenter). + SetButtonBackgroundColor(styles.ButtonBgColor.Color()). + SetButtonTextColor(styles.ButtonFgColor.Color()). + SetLabelColor(styles.LabelFgColor.Color()). + SetFieldTextColor(styles.FieldFgColor.Color()) + f.AddInputField("Replicas:", factor, 4, func(textToCheck string, lastChar rune) bool { _, err := strconv.Atoi(textToCheck) return err == nil @@ -138,6 +143,12 @@ func (s *ScaleExtender) makeScaleForm(sels []string) (*tview.Form, error) { } } + for i := 0; i < f.GetButtonCount(); i++ { + f.GetButton(i). + SetBackgroundColorActivated(styles.ButtonFocusBgColor.Color()). + SetLabelColorActivated(styles.ButtonFocusFgColor.Color()) + } + return f, nil } @@ -145,18 +156,6 @@ func (s *ScaleExtender) dismissDialog() { s.App().Content.RemovePage(scaleDialogKey) } -func (s *ScaleExtender) makeStyledForm(styles config.Dialog) *tview.Form { - f := tview.NewForm() - f.SetItemPadding(0) - f.SetButtonsAlign(tview.AlignCenter). - SetButtonBackgroundColor(styles.ButtonBgColor.Color()). - SetButtonTextColor(styles.ButtonBgColor.Color()). - SetLabelColor(styles.LabelFgColor.Color()). - SetFieldTextColor(styles.FieldFgColor.Color()) - - return f -} - func (s *ScaleExtender) scale(ctx context.Context, path string, replicas int) error { res, err := dao.AccessorFor(s.App().factory, s.GVR()) if err != nil {