main menu: add new redesigned main menu

Signed-off-by: Leandro Dorileo <leandro.maciel.dorileo@intel.com>
This commit is contained in:
Leandro Dorileo
2018-09-11 14:41:59 -07:00
committed by Leandro Dorileo
parent c2654ea92f
commit 68bd76bea6
25 changed files with 828 additions and 232 deletions

View File

@@ -25,7 +25,7 @@ import (
// Also used by the Makefile for releases.
// Default to the version of the program
// but may be overridden for demo/documentation mode.
var Version = "0.8.0"
var Version = "0.9.0"
// SystemInstall represents the system install "configuration", the target
// medias, bundles to install and whatever state a install may require

View File

@@ -102,14 +102,14 @@ Tab.ViewBack = white
Tab.ViewText = blue
// ---- Main menu items ---------------------
MenuButtonText = white bold
MenuButtonBack = blue
Main.MenuText = white bold
Main.MenuBack = blue
MenuButtonActiveText=blue bold
MenuButtonActiveBack=white
Main.MenuActiveText=blue bold
Main.MenuActiveBack=white
SubMenuButtonText=white
SubMenuButtonBack=blue
Main.MenuContentText=white
Main.MenuContentBack=blue
SubMenuButtonActiveText=blue
SubMenuButtonActiveBack=white
Main.MenuContentActiveText=blue
Main.MenuContentActiveBack=white

View File

@@ -1,97 +0,0 @@
// Copyright © 2018 Intel Corporation
//
// SPDX-License-Identifier: GPL-3.0-only
package tui
import (
"fmt"
"github.com/VladimirMarkelov/clui"
)
// AdvancedSubMenuPage is the Page implementation for Advance/Optional configuration options
type AdvancedSubMenuPage struct {
BasePage
btns []*SimpleButton
}
func (page *AdvancedSubMenuPage) addMenuItem(item Page) bool {
buttonPrefix := page.GetButtonPrefix(item)
title := fmt.Sprintf(" %s %s", buttonPrefix, item.GetMenuTitle())
btn := CreateSimpleButton(page.content, AutoSize, AutoSize, title, Fixed)
btn.SetStyle("Menu")
btn.SetAlign(AlignLeft)
btn.OnClick(func(ev clui.Event) {
page.GotoPage(item.GetID())
})
page.btns = append(page.btns, btn)
return buttonPrefix != MenuButtonPrefixUncompleted
}
// Activate is called when the page is "shown" and it repaints the main menu based on the
// available menu pages and their done/undone status
func (page *AdvancedSubMenuPage) Activate() {
for _, curr := range page.btns {
curr.Destroy()
}
page.btns = []*SimpleButton{}
previous := false
activeSet := false
for _, curr := range page.tui.pages {
// Skip Menu Pages that are not required
if curr.IsRequired() || curr.GetMenuTitle() == "" {
continue
}
if page.tui.prevPage != nil {
// Is this menu option match the previous page?
previous = page.tui.prevPage.GetID() == curr.GetID()
}
// Does the menu item added have the data set completed?
completed := page.addMenuItem(curr)
// If we haven't found the first active choice, set it
if !activeSet && !completed {
// Make last button added Active
page.activated = page.btns[len(page.btns)-1]
activeSet = true
}
// Special case if the previous page and the data set is not completed
// we want THIS to be the active choice for easy return
if previous && !completed {
// Make last button added Active
page.activated = page.btns[len(page.btns)-1]
activeSet = true
}
}
}
const (
advancedDesc = `Advanced/Optional configuration items which influence the Installer. Use <Tab> or arrow keys (up and down) to navigate between the elements.`
)
// The Advanced page gives the user the option so select how to set the storage device,
// if to manually configure it or a guided standard partition schema
func newAdvancedPage(tui *Tui) (Page, error) {
page := &AdvancedSubMenuPage{
BasePage: BasePage{
// Tag this Page as required to be complete for the Install to proceed
required: true,
},
}
page.setupMenu(tui, TuiPageAdvancedMenu, "Advanced/Optional Menu", BackButton, TuiPageMenu)
lbl := clui.CreateLabel(page.content, 70, 3, advancedDesc, Fixed)
lbl.SetMultiline(true)
return page, nil
}

View File

@@ -33,11 +33,19 @@ To enable post-installation, use:
`
)
// GetConfiguredValue Returns the string representation of currently value set
func (aup *AutoUpdatePage) GetConfiguredValue() string {
if aup.getModel().AutoUpdate {
return "Enabled"
}
return "Disabled"
}
func newAutoUpdatePage(tui *Tui) (Page, error) {
page := &AutoUpdatePage{}
page.setupMenu(tui, TuiPageAutoUpdate, "Automatic OS Updates",
BackButton|DoneButton, TuiPageAdvancedMenu)
BackButton|DoneButton, TuiPageMenu)
lbl := clui.CreateLabel(page.content, 2, 16, autoUpdateHelp, Fixed)
lbl.SetMultiline(true)

View File

@@ -6,6 +6,7 @@ package tui
import (
"fmt"
"strings"
"github.com/VladimirMarkelov/clui"
"github.com/clearlinux/clr-installer/swupd"
@@ -26,6 +27,11 @@ var (
bundles = []*BundleCheck{}
)
// GetConfiguredValue Returns the string representation of currently value set
func (bp *BundlePage) GetConfiguredValue() string {
return strings.Join(bp.getModel().Bundles, ", ")
}
// Activate marks the checkbox selections based on the data model
func (bp *BundlePage) Activate() {
model := bp.getModel()
@@ -52,7 +58,7 @@ func newBundlePage(tui *Tui) (Page, error) {
}
page := &BundlePage{}
page.setupMenu(tui, TuiPageBundle, "Bundle Selection", NoButtons, TuiPageAdvancedMenu)
page.setupMenu(tui, TuiPageBundle, "Bundle Selection", NoButtons, TuiPageMenu)
clui.CreateLabel(page.content, 2, 2, "Select additional bundles to be added to the system", Fixed)
@@ -74,7 +80,7 @@ func newBundlePage(tui *Tui) (Page, error) {
cancelBtn := CreateSimpleButton(page.cFrame, AutoSize, AutoSize, "Cancel", Fixed)
cancelBtn.OnClick(func(ev clui.Event) {
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
confirmBtn := CreateSimpleButton(page.cFrame, AutoSize, AutoSize, "Confirm", Fixed)
@@ -90,7 +96,7 @@ func newBundlePage(tui *Tui) (Page, error) {
}
page.SetDone(anySelected)
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
return page, nil

View File

@@ -5,9 +5,6 @@
package tui
import (
"reflect"
"strings"
"github.com/clearlinux/clr-installer/model"
"github.com/VladimirMarkelov/clui"
@@ -17,21 +14,21 @@ import (
// BasePage is the common implementation for the TUI frontend
// other pages will inherit this base page behaviours
type BasePage struct {
tui *Tui // the Tui frontend reference
window *clui.Window // the page window
mFrame *clui.Frame // main frame
content *clui.Frame // the main content frame
cFrame *clui.Frame // control frame
cancelBtn *SimpleButton // cancel button
backBtn *SimpleButton // back button
doneBtn *SimpleButton // done button
activated clui.Control // activated control
menuTitle string // the title used to show on main menu
done bool // marks if an item is completed
id int // the page id
data interface{} // arbitrary page context data
action int // indicates if the user has performed a navigation action
required bool // marks if an item is required for the install
tui *Tui // the Tui frontend reference
window *clui.Window // the page window
content *clui.Frame // the main content frame
cFrame *clui.Frame // control frame
cancelBtn *SimpleButton // cancel button
backBtn *SimpleButton // back button
doneBtn *SimpleButton // done button
activated clui.Control // activated control
menuTitle string // the title used to show on main menu
done bool // marks if an item is completed
id int // the page id
data interface{} // arbitrary page context data
action int // indicates if the user has performed a navigation action
required bool // marks if an item is required for the install
menuButton *MenuButton
}
// Page defines the methods a Page must implement
@@ -48,7 +45,9 @@ type Page interface {
Activate()
DeActivate()
GetConfigDefinition() int
GetButtonPrefix(item Page) string
GetConfiguredValue() string
SetMenuButton(mb *MenuButton)
GetMenuButton() *MenuButton
}
const (
@@ -57,14 +56,8 @@ const (
// WindowHeight is our desired terminal width
WindowHeight = 24
// MenuButtonPrefixUncompleted is the standard, uncompleted prefix for a menu button
MenuButtonPrefixUncompleted = "[ ]"
// MenuButtonPrefixCompletedByConfig is the completed by config prefix for a menu button
MenuButtonPrefixCompletedByConfig = "[*]"
// MenuButtonPrefixCompletedByUser is the completed by user prefix for a menu button
MenuButtonPrefixCompletedByUser = "[+]"
// MenuButtonPrefixSubMenu is the prefix for a sub-menu button
MenuButtonPrefixSubMenu = "-->"
// ContentHeight is content frame height
ContentHeight = 15
// AutoSize is shortcut for clui.AutoSize flag
AutoSize = clui.AutoSize
@@ -150,9 +143,6 @@ const (
// TuiPageKernel is the id for the kernel selection page
TuiPageKernel
// TuiPageAdvancedMenu is the id for Advanced/Optional configuration menu
TuiPageAdvancedMenu
// TuiPageSwupdMirror is the id for the swupd mirror page
TuiPageSwupdMirror
@@ -195,6 +185,21 @@ func isPopUpPage(id int) bool {
return false
}
// SetMenuButton sets the page's menu control
func (page *BasePage) SetMenuButton(mb *MenuButton) {
page.menuButton = mb
}
// GetMenuButton returns the page's menu control
func (page *BasePage) GetMenuButton() *MenuButton {
return page.menuButton
}
// GetConfiguredValue Returns the string representation of currently value set
func (page *BasePage) GetConfiguredValue() string {
return "Unknown value"
}
// GetConfigDefinition is a stub implementation
// the real implementation must check with the model and return:
// + ConfigDefinedByUser: if the configuration was interactively defined by the user
@@ -267,23 +272,17 @@ func (page *BasePage) IsRequired() bool {
return page.required
}
// GetButtonPrefix returns string for prefixing a menu button
func (page *BasePage) GetButtonPrefix(item Page) string {
prefix := MenuButtonPrefixUncompleted
// GetMenuStatus returns the menu button status id
func GetMenuStatus(item Page) int {
res := MenuButtonStatusDefault
if item.GetDone() {
prefix = MenuButtonPrefixCompletedByUser
res = MenuButtonStatusUserDefined
} else if item.GetConfigDefinition() == ConfigDefinedByConfig {
prefix = MenuButtonPrefixCompletedByConfig
res = MenuButtonStatusAutoDetect
}
itemType := reflect.TypeOf(item).Elem().Name()
if strings.Contains(itemType, "SubMenu") {
prefix = MenuButtonPrefixSubMenu
}
return prefix
return res
}
func (page *BasePage) setupMenu(tui *Tui, id int, menuTitle string, btns int, returnID int) {
@@ -295,19 +294,19 @@ func (page *BasePage) setup(tui *Tui, id int, btns int, returnID int) {
page.action = ActionNone
page.id = id
page.tui = tui
page.newWindow()
page.window.SetPack(clui.Vertical)
page.mFrame = clui.CreateFrame(page.window, 78, 22, BorderNone, clui.Fixed)
page.mFrame.SetPack(clui.Vertical)
page.content = clui.CreateFrame(page.mFrame, 8, 21, BorderNone, clui.Fixed)
page.content = clui.CreateFrame(page.window, AutoSize, ContentHeight,
BorderNone, clui.Fixed)
page.content.SetPack(clui.Vertical)
page.content.SetPaddings(2, 1)
page.cFrame = clui.CreateFrame(page.mFrame, AutoSize, 1, BorderNone, Fixed)
page.cFrame = clui.CreateFrame(page.window, AutoSize, 1, BorderNone, Fixed)
page.cFrame.SetPack(clui.Horizontal)
page.cFrame.SetGaps(1, 1)
page.cFrame.SetPaddings(2, 0)
page.cFrame.SetPaddings(3, 0)
if btns&CancelButton == CancelButton {
page.newCancelButton(returnID)
@@ -321,6 +320,12 @@ func (page *BasePage) setup(tui *Tui, id int, btns int, returnID int) {
page.newDoneButton(tui, returnID)
}
frm := clui.CreateFrame(page.window, AutoSize, 1, BorderNone, Fixed)
frm.SetPaddings(3, 1)
clui.CreateLabel(frm, AutoSize, 1,
"Use [Tab] or the arrow keys [up and down] to navigate", Fixed)
page.window.SetVisible(false)
}
@@ -330,7 +335,9 @@ func (page *BasePage) newWindow() {
x := (sw - WindowWidth) / 2
y := (sh - WindowHeight) / 2
page.window = clui.AddWindow(x, y, WindowWidth, WindowHeight, " [Clear Linux Installer ("+model.Version+")] ")
page.window = clui.AddWindow(x, y, WindowWidth, WindowHeight,
" [Clear Linux Installer ("+model.Version+")] ")
page.window.SetTitleButtons(0)
page.window.SetSizable(false)
page.window.SetMovable(false)
@@ -342,6 +349,7 @@ func (page *BasePage) newWindow() {
y := (evt.Height - wh) / 2
page.window.SetPos(x, y)
page.window.ResizeChildren()
page.window.PlaceChildren()
})
}

View File

@@ -5,6 +5,8 @@
package tui
import (
"strings"
"github.com/clearlinux/clr-installer/storage"
"github.com/VladimirMarkelov/clui"
@@ -20,6 +22,29 @@ const (
can manually set your own.`
)
// GetConfiguredValue Returns the string representation of currently value set
func (page *DiskMenuPage) GetConfiguredValue() string {
if len(page.getModel().TargetMedias) == 0 {
return "No media configured"
}
res := []string{}
for _, curr := range page.getModel().TargetMedias {
for _, part := range curr.Children {
tks := []string{part.Name, part.FsType}
if part.MountPoint != "" {
tks = append(tks, part.MountPoint)
}
res = append(res, strings.Join(tks, ":"))
}
}
return strings.Join(res, ", ")
}
// GetConfigDefinition returns if the config was interactively defined by the user,
// was loaded from a config file or if the config is not set.
func (page *DiskMenuPage) GetConfigDefinition() int {

View File

@@ -20,6 +20,17 @@ type HostnamePage struct {
userDefined bool
}
// GetConfiguredValue Returns the string representation of currently value set
func (page *HostnamePage) GetConfiguredValue() string {
hn := page.getModel().Hostname
if hn == "" {
return "No target system hostname assigned"
}
return hn
}
// GetConfigDefinition returns if the config was interactively defined by the user,
// was loaded from a config file or if the config is not set.
func (page *HostnamePage) GetConfigDefinition() int {
@@ -50,7 +61,7 @@ func (page *HostnamePage) setConfirmButton() {
func newHostnamePage(tui *Tui) (Page, error) {
page := &HostnamePage{}
page.setupMenu(tui, TuiPageHostname, "Assign Hostname", NoButtons, TuiPageAdvancedMenu)
page.setupMenu(tui, TuiPageHostname, "Assign Hostname", NoButtons, TuiPageMenu)
clui.CreateLabel(page.content, 2, 2, "Assign a Hostname for the installation target", Fixed)
@@ -89,7 +100,7 @@ func newHostnamePage(tui *Tui) (Page, error) {
page.cancelBtn = CreateSimpleButton(page.cFrame, AutoSize, AutoSize, "Cancel", Fixed)
page.cancelBtn.OnClick(func(ev clui.Event) {
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
page.confirmBtn = CreateSimpleButton(page.cFrame, AutoSize, AutoSize, "Confirm", Fixed)
@@ -105,7 +116,7 @@ func newHostnamePage(tui *Tui) (Page, error) {
}
page.getModel().Hostname = hostname
page.setConfirmButton()
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
page.confirmBtn.SetEnabled(false)

View File

@@ -14,6 +14,17 @@ type KernelCMDLine struct {
kernelCMDLineEdit *clui.EditField
}
// GetConfiguredValue Returns the string representation of currently value set
func (pp *KernelCMDLine) GetConfiguredValue() string {
cmdLine := pp.getModel().KernelCMDLine
if cmdLine == "" {
return "No kernel command line append set"
}
return cmdLine
}
// Activate sets the kernel cmd line configuration with the current model's value
func (pp *KernelCMDLine) Activate() {
pp.kernelCMDLineEdit.SetTitle(pp.getModel().KernelCMDLine)
@@ -21,7 +32,7 @@ func (pp *KernelCMDLine) Activate() {
func newKernelCMDLine(tui *Tui) (Page, error) {
page := &KernelCMDLine{}
page.setupMenu(tui, TuiPageKernelCMDLine, "Kernel Command Line", NoButtons, TuiPageAdvancedMenu)
page.setupMenu(tui, TuiPageKernelCMDLine, "Kernel Command Line", NoButtons, TuiPageMenu)
clui.CreateLabel(page.content, 2, 2, "Configure the Kernel Command Line", Fixed)
@@ -49,7 +60,7 @@ func newKernelCMDLine(tui *Tui) (Page, error) {
cancelBtn := CreateSimpleButton(btnFrm, AutoSize, AutoSize, "Cancel", Fixed)
cancelBtn.OnClick(func(ev clui.Event) {
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
confirmBtn := CreateSimpleButton(btnFrm, AutoSize, AutoSize, "Confirm", Fixed)
@@ -57,7 +68,7 @@ func newKernelCMDLine(tui *Tui) (Page, error) {
confirmBtn.OnClick(func(ev clui.Event) {
page.getModel().KernelCMDLine = page.kernelCMDLineEdit.Title()
page.SetDone(page.kernelCMDLineEdit.Title() != "")
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
page.activated = page.kernelCMDLineEdit

View File

@@ -24,6 +24,11 @@ type KernelRadio struct {
radio *clui.Radio
}
// GetConfiguredValue Returns the string representation of currently value set
func (kp *KernelPage) GetConfiguredValue() string {
return kp.getModel().Kernel.Bundle
}
// Activate marks selects the kernel radio based on the data model
func (kp *KernelPage) Activate() {
model := kp.getModel()
@@ -64,7 +69,7 @@ func newKernelPage(tui *Tui) (Page, error) {
page.kernels = append(page.kernels, &KernelRadio{curr, nil})
}
page.setupMenu(tui, TuiPageKernel, "Kernel Selection", NoButtons, TuiPageAdvancedMenu)
page.setupMenu(tui, TuiPageKernel, "Kernel Selection", NoButtons, TuiPageMenu)
clui.CreateLabel(page.content, 2, 2, "Select desired kernel", Fixed)
frm := clui.CreateFrame(page.content, AutoSize, AutoSize, BorderNone, Fixed)
@@ -88,7 +93,7 @@ func newKernelPage(tui *Tui) (Page, error) {
cancelBtn := CreateSimpleButton(page.cFrame, AutoSize, AutoSize, "Cancel", Fixed)
cancelBtn.OnClick(func(ev clui.Event) {
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
confirmBtn := CreateSimpleButton(page.cFrame, AutoSize, AutoSize, "Confirm", Fixed)
@@ -96,7 +101,7 @@ func newKernelPage(tui *Tui) (Page, error) {
selected := page.group.Selected()
page.getModel().Kernel = page.kernels[selected].kernel
page.SetDone(true)
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
return page, nil

View File

@@ -17,6 +17,11 @@ type KeyboardPage struct {
kbdListBox *clui.ListBox
}
// GetConfiguredValue Returns the string representation of currently keyboard set
func (page *KeyboardPage) GetConfiguredValue() string {
return page.getModel().Keyboard.Code
}
// GetConfigDefinition returns if the config was interactively defined by the user,
// was loaded from a config file or if the config is not set.
func (page *KeyboardPage) GetConfigDefinition() int {
@@ -107,11 +112,12 @@ func newKeyboardPage(tui *Tui) (Page, error) {
}
})
frame := clui.CreateFrame(page.content, AutoSize, 7, BorderNone, Fixed)
frame := clui.CreateFrame(page.content, AutoSize, AutoSize, BorderNone, Fixed)
frame.SetPack(clui.Vertical)
frame.SetPaddings(0, 1)
clui.CreateLabel(frame, AutoSize, 1, "Test keyboard", Fixed)
lbl = clui.CreateLabel(frame, AutoSize, 1, "Test keyboard", Fixed)
lbl.SetPaddings(0, 1)
newEditField(frame, false, nil)
page.activated = page.doneBtn

View File

@@ -17,6 +17,11 @@ type LanguagePage struct {
langListBox *clui.ListBox
}
// GetConfiguredValue Returns the string representation of currently language set
func (page *LanguagePage) GetConfiguredValue() string {
return page.getModel().Language.String()
}
// GetConfigDefinition returns if the config was interactively defined by the user,
// was loaded from a config file or if the config is not set.
func (page *LanguagePage) GetConfigDefinition() int {
@@ -73,7 +78,7 @@ func newLanguagePage(tui *Tui) (Page, error) {
lbl := clui.CreateLabel(page.content, 2, 2, "Select System Language", Fixed)
lbl.SetPaddings(0, 2)
page.langListBox = clui.CreateListBox(page.content, AutoSize, 17, Fixed)
page.langListBox = clui.CreateListBox(page.content, AutoSize, ContentHeight-1, Fixed)
defLanguage := 0
for idx, curr := range page.avLanguages {

View File

@@ -5,74 +5,95 @@
package tui
import (
"fmt"
"time"
"github.com/VladimirMarkelov/clui"
"github.com/clearlinux/clr-installer/controller"
"github.com/VladimirMarkelov/clui"
)
// MenuPage is the Page implementation for the main menu page
type MenuPage struct {
BasePage
btns []*SimpleButton
installBtn *SimpleButton
tabGroup *TabGroup
reqTab *TabPage
advTab *TabPage
}
func (page *MenuPage) addMenuItem(item Page) bool {
func (page *MenuPage) addMenuItem(item Page, tab *TabPage) *MenuButton {
fw, _ := tab.frame.Size()
buttonPrefix := page.GetButtonPrefix(item)
title := fmt.Sprintf(" %s %s", buttonPrefix, item.GetMenuTitle())
btn := CreateSimpleButton(page.content, AutoSize, AutoSize, title, Fixed)
btn.SetStyle("Menu")
btn := CreateMenuButton(tab.frame, MenuButtonStatusDefault, item.GetMenuTitle(), fw)
btn.SetStyle("Main")
btn.SetAlign(AlignLeft)
btn.SetActive(false)
item.SetMenuButton(btn)
btn.OnClick(func(ev clui.Event) {
tab.activeMenu = btn
page.GotoPage(item.GetID())
})
page.btns = append(page.btns, btn)
return buttonPrefix != MenuButtonPrefixUncompleted
return btn
}
// Activate is called when the page is "shown" and it repaints the main menu based on the
// available menu pages and their done/undone status
func (page *MenuPage) Activate() {
for _, curr := range page.btns {
curr.Destroy()
}
page.btns = []*SimpleButton{}
previous := false
activeSet := false
// if we're returning to the "advanced" tab then simply sets the previously
// active menu item
if page.advTab.IsVisible() {
page.activated = page.advTab.activeMenu
activeSet = true
}
// if we're returning to the "required" tab then we iterate over not yet
// completed required "tasks" and select the missing one
for _, curr := range page.tui.pages {
// Skip Menu Pages that are not required
if !curr.IsRequired() {
if curr.GetMenuTitle() == "" || curr.GetID() == page.GetID() {
continue
}
tab := page.reqTab
if !curr.IsRequired() {
tab = page.advTab
}
if page.tui.prevPage != nil {
// Is this menu option match the previous page?
previous = page.tui.prevPage.GetID() == curr.GetID()
}
btn := curr.GetMenuButton()
if btn == nil {
btn = page.addMenuItem(curr, tab)
}
btn.SetMenuItemValue(curr.GetConfiguredValue())
btn.SetStatus(GetMenuStatus(curr))
// Does the menu item added have the data set completed?
completed := page.addMenuItem(curr)
completed := GetMenuStatus(curr) != MenuButtonStatusDefault
// If we haven't found the first active choice, set it
if !activeSet && !completed {
// Make last button added Active
page.activated = page.btns[len(page.btns)-1]
page.activated = btn
activeSet = true
}
// Special case if the previous page and the data set is not completed
// we want THIS to be the active choice for easy return
if previous && !completed {
if previous && !completed && !activeSet {
// Make last button added Active
page.activated = page.btns[len(page.btns)-1]
page.activated = btn
activeSet = true
}
}
@@ -80,22 +101,50 @@ func (page *MenuPage) Activate() {
if page.getModel() != nil && page.getModel().Validate() == nil {
page.installBtn.SetEnabled(true)
page.activated = page.installBtn
} else {
scrollTabToActive(page.activated, page.tabGroup)
}
}
const (
menuHelp = `Choose the next steps. Use <Tab> or arrow keys (up and down) to navigate
between the elements.
`
)
func scrollTabToActive(activated clui.Control, group *TabGroup) {
if activated == nil {
return
}
vFrame := group.GetVisibleFrame()
_, cy, _, ch := vFrame.Clipper()
vx, vy := vFrame.Pos()
_, ay := activated.Pos()
_, ah := activated.Size()
if ay+ah > cy+ch || ay < cy {
diff := (cy + ch) - (ay + ah)
ty := vy + diff
vFrame.ScrollTo(vx, ty)
}
}
func newMenuPage(tui *Tui) (Page, error) {
var err error
page := &MenuPage{}
page.setup(tui, TuiPageMenu, NoButtons, TuiPageMenu)
lbl := clui.CreateLabel(page.content, 2, 3, menuHelp, Fixed)
lbl.SetMultiline(true)
lbl.SetPaddings(0, 2)
// the menu is an special case, we have no paddings
page.content.SetPaddings(0, 0)
page.tabGroup = NewTabGroup(page.content, 1, ContentHeight)
page.reqTab, err = page.tabGroup.AddTab("Required options", 'r')
if err != nil {
return nil, err
}
page.advTab, err = page.tabGroup.AddTab("Advanced options", 'a')
if err != nil {
return nil, err
}
cancelBtn := CreateSimpleButton(page.cFrame, AutoSize, AutoSize, "Cancel", Fixed)
cancelBtn.OnClick(func(ev clui.Event) {

233
tui/menu_button.go Normal file
View File

@@ -0,0 +1,233 @@
// Copyright © 2018 Intel Corporation
//
// SPDX-License-Identifier: GPL-3.0-only
package tui
import (
"fmt"
"github.com/VladimirMarkelov/clui"
xs "github.com/huandu/xstrings"
term "github.com/nsf/termbox-go"
"sync/atomic"
"time"
)
// MenuButton is the implementation of a clui button with simpler
// visual elements (i.e no shadows).
type MenuButton struct {
clui.BaseControl
itemValue string
pressed int32
onClick func(clui.Event)
status int
sWdith int // splitter width
}
const (
textPadding = 3
// MenuButtonStatusDefault means: not the auto detect,
// not the user defined, not even failed
MenuButtonStatusDefault = iota
// MenuButtonStatusAutoDetect means: the current value was autodetected
MenuButtonStatusAutoDetect
// MenuButtonStatusUserDefined means: the user actively changed the value
MenuButtonStatusUserDefined
//MenuButtonStatusFailure means: the label displayed is an error message
MenuButtonStatusFailure
)
var (
// by now we're using the same char for everything
statusSymbol = map[int]rune{
MenuButtonStatusDefault: '»',
MenuButtonStatusUserDefined: '»',
MenuButtonStatusFailure: '»',
MenuButtonStatusAutoDetect: '»',
}
)
// SetStatus sets the status attribute for a menu button
func (mb *MenuButton) SetStatus(status int) {
mb.status = status
}
// Status returns the currently set status for a menu button
func (mb *MenuButton) Status() int {
return mb.status
}
// SetMenuItemValue sets the value for the itemValue (configured value for a menu item)
func (mb *MenuButton) SetMenuItemValue(sv string) {
mb.itemValue = sv
}
// MenuItemValue returns the value set for itemValue (configured value for a menu item)
func (mb *MenuButton) MenuItemValue() string {
return mb.itemValue
}
// CreateMenuButton returns an instance of MenuButton
// parent - is the parent control this button is attached to
// status - one of: MenuButtonStatusDefault, MenuButtonStatusUserDefined, MenuButtonStatusFailure
// title - the button's label
func CreateMenuButton(parent clui.Control, status int, title string, sWidth int) *MenuButton {
mb := new(MenuButton)
mb.sWdith = sWidth
mb.BaseControl = clui.NewBaseControl()
mb.SetParent(parent)
mb.SetAlign(clui.AlignLeft)
mb.status = status
height := 4
width := xs.Len(title) + 2
mb.SetTitle(title)
mb.SetSize(width, height)
mb.SetConstraints(width, height)
mb.SetScale(Fixed)
if parent != nil {
parent.AddChild(mb)
}
return mb
}
// Draw paints the button in the screen and adjust colors depending on the button state
func (mb *MenuButton) Draw() {
clui.PushAttributes()
defer clui.PopAttributes()
x, y := mb.Pos()
w, h := mb.Size()
fg, bg := mb.TextColor(), mb.BackColor()
sfg, sbg := mb.TextColor(), mb.BackColor()
if mb.Active() {
fgActive, bgActive := mb.ActiveColors()
fg = clui.RealColor(fgActive, mb.Style(), "MenuActiveText")
bg = clui.RealColor(bgActive, mb.Style(), "MenuActiveBack")
sfg = clui.RealColor(sfg, mb.Style(), "MenuContentActiveText")
sbg = clui.RealColor(sbg, mb.Style(), "MenuContentActiveBack")
} else {
fg = clui.RealColor(fg, mb.Style(), "MenuText")
bg = clui.RealColor(bg, mb.Style(), "MenuBack")
sfg = clui.RealColor(sfg, mb.Style(), "MenuContentText")
sbg = clui.RealColor(sbg, mb.Style(), "MenuContentBack")
}
clui.SetTextColor(fg)
shift, text := clui.AlignColorizedText(mb.Title(), w, mb.Align())
clui.SetBackColor(bg)
clui.FillRect(x, y, w, h, ' ')
clui.DrawText(x+shift+textPadding, y+1, text)
clui.PopAttributes()
clui.PushAttributes()
itemValue := fmt.Sprintf("%c %s", statusSymbol[mb.status], mb.itemValue)
shift, itemText := clui.AlignColorizedText(itemValue, w, mb.Align())
clui.SetBackColor(sbg)
clui.SetTextColor(sfg)
clui.DrawText(x+shift+textPadding, y+2, itemText)
if !mb.Active() {
for i := 0; i < mb.sWdith; i++ {
clui.DrawText(x+shift+textPadding+i, y+3, "_")
}
}
}
func (mb *MenuButton) isPressed() int32 {
return atomic.LoadInt32(&mb.pressed)
}
func (mb *MenuButton) setPressed(pressed int32) {
atomic.StoreInt32(&mb.pressed, pressed)
}
func (mb *MenuButton) processKeyEvent(event clui.Event) bool {
ekey := event.Key
if (ekey == term.KeySpace || ekey == term.KeyEnter) && mb.isPressed() == 0 {
mb.setPressed(1)
ev := clui.Event{Type: clui.EventRedraw}
go func() {
clui.PutEvent(ev)
time.Sleep(100 * time.Millisecond)
mb.setPressed(0)
clui.PutEvent(ev)
}()
if mb.onClick != nil {
mb.onClick(event)
}
return true
} else if ekey == term.KeyEsc && mb.isPressed() != 0 {
mb.setPressed(0)
clui.ReleaseEvents()
return true
}
return false
}
func (mb *MenuButton) processMouseEvent(event clui.Event) bool {
if event.Key == term.MouseLeft {
mb.setPressed(1)
clui.GrabEvents(mb)
return true
} else if event.Key == term.MouseRelease && mb.isPressed() != 0 {
clui.ReleaseEvents()
bX, bY := mb.Pos()
bw, bh := mb.Size()
if event.X >= bX && event.Y >= bY && event.X < bX+bw && event.Y < bY+bh {
if mb.onClick != nil {
mb.onClick(event)
}
}
mb.setPressed(0)
return true
}
return false
}
// ProcessEvent will process the events triggered by clui mainloop
func (mb *MenuButton) ProcessEvent(event clui.Event) bool {
if !mb.Enabled() {
return false
}
if event.Type == clui.EventKey {
return mb.processKeyEvent(event)
} else if event.Type == clui.EventMouse {
return mb.processMouseEvent(event)
}
return false
}
// OnClick sets the button's onClick callback
func (mb *MenuButton) OnClick(fn func(clui.Event)) {
mb.onClick = fn
}

View File

@@ -6,6 +6,7 @@ package tui
import (
"fmt"
"strings"
"github.com/clearlinux/clr-installer/network"
@@ -21,6 +22,34 @@ type NetworkPage struct {
interfaces []*network.Interface
}
// GetConfiguredValue Returns the string representation of currently value set
func (page *NetworkPage) GetConfiguredValue() string {
var err error
ifaces := page.getModel().NetworkInterfaces
if len(ifaces) == 0 {
ifaces, err = network.Interfaces()
if err != nil {
return "Could not load network interfaces"
}
}
res := []string{}
for _, curr := range ifaces {
for _, addr := range curr.Addrs {
if addr.Version != network.IPv4 {
continue
}
tks := []string{addr.IP, addr.NetMask}
res = append(res, strings.Join(tks, ":"))
}
}
return strings.Join(res, ", ")
}
// GetConfigDefinition returns if the config was interactively defined by the user,
// was loaded from a config file or if the config is not set.
func (page *NetworkPage) GetConfigDefinition() int {
@@ -99,7 +128,7 @@ func (page *NetworkPage) Activate() {
func newNetworkPage(tui *Tui) (Page, error) {
page := &NetworkPage{}
page.setupMenu(tui, TuiPageNetwork, "Configure network interfaces",
BackButton, TuiPageAdvancedMenu)
BackButton, TuiPageMenu)
page.frm = clui.CreateFrame(page.content, AutoSize, AutoSize, BorderNone, Fixed)
page.frm.SetPack(clui.Vertical)

View File

@@ -157,7 +157,7 @@ func validateIPEdit(k term.Key, ch rune) bool {
func newNetworkInterfacePage(tui *Tui) (Page, error) {
page := &NetworkInterfacePage{}
page.setup(tui, TuiPageInterface, NoButtons, TuiPageAdvancedMenu)
page.setup(tui, TuiPageInterface, NoButtons, TuiPageMenu)
frm := clui.CreateFrame(page.content, AutoSize, AutoSize, BorderNone, Fixed)
frm.SetPack(clui.Horizontal)

View File

@@ -16,12 +16,13 @@ import (
// the Page interface, but does not allocate a Window. Instead it will launch
// a modal PopUp window.
type NetworkValidatePage struct {
tui *Tui // the Tui frontend reference
menuTitle string // the title used to show on main menu
done bool // marks if an item is completed
id int // the page id
data interface{} // arbitrary page context data
required bool // marks if an item is required for the install
tui *Tui // the Tui frontend reference
menuTitle string // the title used to show on main menu
done bool // marks if an item is completed
id int // the page id
data interface{} // arbitrary page context data
required bool // marks if an item is required for the install
menuButton *MenuButton // the menu button reference
}
// GetID returns the current page's identifier
@@ -74,7 +75,7 @@ func (page *NetworkValidatePage) GetDone() bool {
func (page *NetworkValidatePage) Activate() {
if dialog, err := CreateNetworkTestDialogBox(page.tui.model); err == nil {
dialog.OnClose(func() {
page.tui.gotoPage(TuiPageAdvancedMenu, page.tui.currPage)
page.tui.gotoPage(TuiPageMenu, page.tui.currPage)
})
result := dialog.RunNetworkTest()
@@ -100,15 +101,24 @@ func (page *NetworkValidatePage) GetConfigDefinition() int {
return ConfigDefinedByUser
}
// GetButtonPrefix returns string for prefixing a menu button
func (page *NetworkValidatePage) GetButtonPrefix(item Page) string {
prefix := MenuButtonPrefixUncompleted
// GetConfiguredValue Returns the string representation of currently value set
func (page *NetworkValidatePage) GetConfiguredValue() string {
return "Check if all the required network settings are properly set"
}
if item.GetDone() {
prefix = MenuButtonPrefixCompletedByUser
}
// GetMenuStatus returns the menu button status id
func (page *NetworkValidatePage) GetMenuStatus(item Page) int {
return MenuButtonStatusDefault
}
return prefix
// GetMenuButton is a page implementation for network validate popup
func (page *NetworkValidatePage) GetMenuButton() *MenuButton {
return page.menuButton
}
// SetMenuButton is a no-op page implementation for network validate popup
func (page *NetworkValidatePage) SetMenuButton(mb *MenuButton) {
page.menuButton = mb
}
func newNetworkValidatePage(tui *Tui) (Page, error) {

View File

@@ -19,6 +19,17 @@ type ProxyPage struct {
confirmBtn *SimpleButton
}
// GetConfiguredValue Returns the string representation of currently value set
func (pp *ProxyPage) GetConfiguredValue() string {
value := pp.getModel().HTTPSProxy
if value == "" {
return "No HTTPS proxy URL set"
}
return value
}
// Activate sets the https proxy with the current model's value
func (pp *ProxyPage) Activate() {
pp.httpsProxyEdit.SetTitle(pp.getModel().HTTPSProxy)
@@ -35,7 +46,7 @@ func (pp *ProxyPage) setConfirmButton() {
func newProxyPage(tui *Tui) (Page, error) {
page := &ProxyPage{}
page.setupMenu(tui, TuiPageProxy, "Proxy", NoButtons, TuiPageAdvancedMenu)
page.setupMenu(tui, TuiPageProxy, "Proxy", NoButtons, TuiPageMenu)
clui.CreateLabel(page.content, 2, 2, "Configure the network proxy", Fixed)
@@ -82,7 +93,7 @@ func newProxyPage(tui *Tui) (Page, error) {
cancelBtn := CreateSimpleButton(btnFrm, AutoSize, AutoSize, "Cancel", Fixed)
cancelBtn.OnClick(func(ev clui.Event) {
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
page.confirmBtn = CreateSimpleButton(btnFrm, AutoSize, AutoSize, "Confirm", Fixed)
@@ -92,7 +103,7 @@ func newProxyPage(tui *Tui) (Page, error) {
page.getModel().HTTPSProxy = proxy
if dialog, err := CreateNetworkTestDialogBox(page.tui.model); err == nil {
dialog.OnClose(func() {
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
if dialog.RunNetworkTest() {
page.SetDone(proxy != "")

View File

@@ -16,8 +16,9 @@ import (
// visual elements (i.e no shadows).
type SimpleButton struct {
clui.BaseControl
pressed int32
onClick func(clui.Event)
pressed int32
onClick func(clui.Event)
forceActiveStyle bool
}
// CreateSimpleButton returns an instance of SimpleButton
@@ -70,7 +71,7 @@ func (b *SimpleButton) Draw() {
if !b.Enabled() {
fg = clui.RealColor(fg, b.Style(), "ButtonDisabledText")
bg = clui.RealColor(bg, b.Style(), "ButtonDisabledBack")
} else if b.Active() {
} else if b.Active() || b.ForceActiveStyle() {
fgActive, bgActive := b.ActiveColors()
fg = clui.RealColor(fgActive, b.Style(), "ButtonActiveText")
@@ -171,3 +172,14 @@ func (b *SimpleButton) ProcessEvent(event clui.Event) bool {
func (b *SimpleButton) OnClick(fn func(clui.Event)) {
b.onClick = fn
}
// SetForceActiveStyle even if the button is not active the user may want
// to force setting the active style
func (b *SimpleButton) SetForceActiveStyle(f bool) {
b.forceActiveStyle = f
}
// ForceActiveStyle returns the forceActiveStyle flag
func (b *SimpleButton) ForceActiveStyle() bool {
return b.forceActiveStyle
}

View File

@@ -22,6 +22,17 @@ type SwupdMirrorPage struct {
userDefined bool
}
// GetConfiguredValue Returns the string representation of currently value set
func (page *SwupdMirrorPage) GetConfiguredValue() string {
mirror := page.getModel().SwupdMirror
if mirror == "" {
return "No swupd mirror set"
}
return mirror
}
// GetConfigDefinition returns if the config was interactively defined by the user,
// was loaded from a config file or if the config is not set.
func (page *SwupdMirrorPage) GetConfigDefinition() int {
@@ -52,7 +63,7 @@ func (page *SwupdMirrorPage) setConfirmButton() {
func newSwupdMirrorPage(tui *Tui) (Page, error) {
page := &SwupdMirrorPage{}
page.setupMenu(tui, TuiPageSwupdMirror, "Swupd Mirror", NoButtons, TuiPageAdvancedMenu)
page.setupMenu(tui, TuiPageSwupdMirror, "Swupd Mirror", NoButtons, TuiPageMenu)
clui.CreateLabel(page.content, 2, 2, "Configure the Installation Source (swupd) Mirror", Fixed)
@@ -101,7 +112,7 @@ func newSwupdMirrorPage(tui *Tui) (Page, error) {
page.cancelBtn = CreateSimpleButton(btnFrm, AutoSize, AutoSize, "Cancel", Fixed)
page.cancelBtn.OnClick(func(ev clui.Event) {
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
})
page.confirmBtn = CreateSimpleButton(btnFrm, AutoSize, AutoSize, "Confirm", Fixed)
@@ -114,7 +125,7 @@ func newSwupdMirrorPage(tui *Tui) (Page, error) {
page.getModel().SwupdMirror = mirror
}
page.SetDone(false)
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
page.userDefined = false
} else {
url, err := swupd.SetHostMirror(mirror)
@@ -127,7 +138,7 @@ func newSwupdMirrorPage(tui *Tui) (Page, error) {
page.userDefined = true
page.getModel().SwupdMirror = mirror
page.SetDone(true)
page.GotoPage(TuiPageAdvancedMenu)
page.GotoPage(TuiPageMenu)
}
}
}

208
tui/tab.go Normal file
View File

@@ -0,0 +1,208 @@
package tui
import (
"fmt"
"unicode"
"github.com/VladimirMarkelov/clui"
"github.com/clearlinux/clr-installer/errors"
)
// TabGroup represents a tab group and holds logically grouped tabs
type TabGroup struct {
mainFrame clui.Control // the widget content frame
btnsFrame clui.Control // this frame holds the buttons elements of the tab component
contentFrame clui.Control // where the contents are stacked
pages []*TabPage // represents the pages - each page contains a button and its content frame
selected *TabPage // currently selected page
height int // the menu frame height
paddingX int // the menu X padding
window clui.Control // the window the tab was added to
}
// TabPage represents an individual element containing basically a hotkey, button and content
type TabPage struct {
hotKey rune // the kotkey char
label string // the button label (non formated)
btn *SimpleButton // the tab button
frame *clui.Frame // the content frame
activeMenu clui.Control
}
// TabKeyCb holds the contexts for the keybinds handling iterations
type TabKeyCb struct {
tab *TabGroup
}
const (
tabButtonHeight = 3
keyBindingDescWidth = 4
tabPadding = 4
)
// NewTabGroup allocates a new TabGroup object and initializes the basic frames and triggers
// the events pooling
func NewTabGroup(parent clui.Control, paddingX int, height int) *TabGroup {
mainFrame := clui.CreateFrame(parent, AutoSize, height, BorderNone, Fixed)
mainFrame.SetPack(clui.Vertical)
btnsFrame := clui.CreateFrame(mainFrame, AutoSize, tabButtonHeight, BorderNone, Fixed)
btnsFrame.SetStyle("Tab")
contentFrame := clui.CreateFrame(mainFrame, AutoSize, height, BorderNone, Fixed)
contentFrame.SetPack(clui.Vertical)
res := &TabGroup{
btnsFrame: btnsFrame,
mainFrame: mainFrame,
contentFrame: contentFrame,
height: height,
paddingX: paddingX,
}
ctrl := parent
for ctrl.Parent() != nil {
ctrl = ctrl.Parent()
}
wnd := ctrl.(*clui.Window)
wnd.OnKeyDown(keyEventCb, &TabKeyCb{tab: res})
res.window = wnd
return res
}
// AddTab allocates a new TagPage, creates button and frame accordingly
func (tg *TabGroup) AddTab(label string, hotKey rune) (*TabPage, error) {
for _, curr := range tg.pages {
if curr.hotKey == hotKey {
return nil, errors.Errorf("Duplicated hotkey between %s and %s", curr.label, label)
}
}
width := len(label) + keyBindingDescWidth + tabPadding
page := &TabPage{hotKey: hotKey, label: label}
active := len(tg.pages) == 0
tg.pages = append(tg.pages, page)
flabel := fmt.Sprintf("[%c] %s", unicode.To(unicode.UpperCase, hotKey), label)
page.btn = CreateSimpleButton(tg.btnsFrame, width, 3, flabel, Fixed)
page.btn.SetForceActiveStyle(active)
page.btn.SetAlign(AlignLeft)
page.btn.SetPaddings(2, 1)
page.btn.SetStyle("Tab")
page.btn.SetTabStop(false)
page.btn.OnClick(func(clui.Event) {
var hidden []*TabPage
for _, curr := range tg.pages {
if curr == page {
continue
}
hidden = append(hidden, curr)
}
tg.setSelected(page, hidden)
})
page.frame = clui.CreateFrame(tg.contentFrame, AutoSize, tg.height, BorderNone, Fixed)
page.frame.SetPack(clui.Vertical)
page.frame.SetPaddings(tg.paddingX, 1)
page.frame.SetScrollable(true)
if active {
tg.selected = page
} else {
page.frame.SetVisible(false)
}
return page, nil
}
func keyEventCb(ev clui.Event, data interface{}) bool {
cbData := data.(*TabKeyCb)
var selected *TabPage
var hidden []*TabPage
for _, curr := range cbData.tab.pages {
if ev.Ch == curr.hotKey || ev.Ch == unicode.ToUpper(curr.hotKey) {
selected = curr
} else {
hidden = append(hidden, curr)
}
}
if selected == nil {
return false
}
cbData.tab.setSelected(selected, hidden)
return true
}
func (tg *TabGroup) setSelected(selected *TabPage, hidden []*TabPage) {
if selected == nil || tg.selected == selected {
return
}
for _, curr := range hidden {
if curr.frame != nil {
curr.activeMenu = clui.ActiveControl(curr.frame)
curr.frame.SetVisible(false)
}
curr.btn.SetForceActiveStyle(false)
}
selected.btn.SetForceActiveStyle(true)
tg.selected = selected
selected.frame.SetVisible(true)
activeMenu := selected.activeMenu
if activeMenu == nil {
for _, curr := range selected.frame.Children() {
if _, ok := curr.(*MenuButton); ok {
activeMenu = curr
break
}
}
}
selected.activeMenu = activeMenu
clui.ActivateControl(tg.window, activeMenu)
activeMenu.SetActive(true)
}
// SetActive sets the nth page of a TabGroup as active
func (tg *TabGroup) SetActive(idx int) error {
if idx > len(tg.pages)-1 || idx < 0 {
return errors.Errorf("Invalid page index: %d", idx)
}
for i, curr := range tg.pages {
curr.btn.SetForceActiveStyle(i == idx)
}
return nil
}
// GetVisibleFrame returns the visible page's content frame
func (tg *TabGroup) GetVisibleFrame() *clui.Frame {
for _, curr := range tg.pages {
if curr.IsVisible() {
return curr.frame
}
}
return nil
}
// IsVisible returns true if a given tab page is visible, and false otherwise
func (tp *TabPage) IsVisible() bool {
return tp.frame.Visible()
}

View File

@@ -5,6 +5,7 @@
package tui
import (
"fmt"
"strings"
"github.com/VladimirMarkelov/clui"
@@ -17,6 +18,23 @@ type TelemetryPage struct {
BasePage
}
// GetConfiguredValue Returns the string representation of currently value set
func (tp *TelemetryPage) GetConfiguredValue() string {
var obs string
var res string
if tp.getModel().Telemetry.Defined {
obs = " (Acknowledgment required)"
}
res = "Disabled"
if tp.getModel().Telemetry.Enabled {
res = "Enabled"
}
return fmt.Sprintf("%s %s", res, obs)
}
func newTelemetryPage(tui *Tui) (Page, error) {
page := &TelemetryPage{
BasePage: BasePage{

View File

@@ -17,6 +17,11 @@ type TimezonePage struct {
tzListBox *clui.ListBox
}
// GetConfiguredValue Returns the string representation of currently timezone set
func (page *TimezonePage) GetConfiguredValue() string {
return page.getModel().Timezone.Code
}
// GetConfigDefinition returns if the config was interactively defined by the user,
// was loaded from a config file or if the config is not set.
func (page *TimezonePage) GetConfigDefinition() int {
@@ -73,7 +78,7 @@ func newTimezonePage(tui *Tui) (Page, error) {
lbl := clui.CreateLabel(page.content, 2, 2, "Select System Timezone", Fixed)
lbl.SetPaddings(0, 2)
page.tzListBox = clui.CreateListBox(page.content, AutoSize, 17, Fixed)
page.tzListBox = clui.CreateListBox(page.content, AutoSize, ContentHeight-1, Fixed)
defTimezone := 0
for idx, curr := range page.avTimezones {

View File

@@ -128,7 +128,6 @@ func (tui *Tui) Run(md *model.SystemInstall, rootDir string) (bool, error) {
{"kernel cmdline", newKernelCMDLine},
{"kernel selection", newKernelPage},
{"install", newInstallPage},
{"advanced menu", newAdvancedPage},
{"swupd mirror", newSwupdMirrorPage},
{"hostname", newHostnamePage},
{"autoupdate", newAutoUpdatePage},

View File

@@ -6,6 +6,7 @@ package tui
import (
"fmt"
"strings"
"github.com/clearlinux/clr-installer/user"
@@ -35,6 +36,28 @@ type UserBtn struct {
btn *SimpleButton
}
// GetConfiguredValue Returns the string representation of currently value set
func (page *UseraddPage) GetConfiguredValue() string {
users := page.getModel().Users
res := []string{}
if len(users) == 0 {
return "No users added"
}
for _, curr := range users {
tks := []string{curr.Login}
if curr.Admin {
tks = append(tks, "admin")
}
res = append(res, strings.Join(tks, ":"))
}
return strings.Join(res, ", ")
}
func (page *UseraddPage) validateLogin() {
showLabel := false
enableConfirm := true
@@ -102,7 +125,7 @@ func (page *UseraddPage) validatePassword() {
func newUseraddPage(tui *Tui) (Page, error) {
page := &UseraddPage{users: []*UserBtn{}}
page.setupMenu(tui, TuiPageUseradd, "Add Users", BackButton, TuiPageAdvancedMenu)
page.setupMenu(tui, TuiPageUseradd, "Add Users", BackButton, TuiPageMenu)
clui.CreateLabel(page.content, 2, 2, "Add new users", Fixed)