mirror of
https://github.com/VladimirMarkelov/clui.git
synced 2025-04-26 13:49:01 +08:00
closes #68
This commit is contained in:
parent
bca28ea40c
commit
9a5b4343de
@ -7,7 +7,7 @@ Command Line User Interface (Console UI inspired by TurboVision) with built-in t
|
|||||||
|
|
||||||
|
|
||||||
## Current version
|
## Current version
|
||||||
The current version is 0.9.0 RC1. Please see details in [changelog](./changelog).
|
The current version is 0.9.0 RC2. Please see details in [changelog](./changelog).
|
||||||
|
|
||||||
## Applications that uses the library
|
## Applications that uses the library
|
||||||
* Terminal FB2 reader(termfb2): https://github.com/VladimirMarkelov/termfb2
|
* Terminal FB2 reader(termfb2): https://github.com/VladimirMarkelov/termfb2
|
||||||
@ -39,6 +39,7 @@ The current version is 0.9.0 RC1. Please see details in [changelog](./changelog)
|
|||||||
* SparkChart (Show tabular data as a bar graph)
|
* SparkChart (Show tabular data as a bar graph)
|
||||||
* GridView (Table to show structured data - only virtual and readonly mode with scroll support)
|
* GridView (Table to show structured data - only virtual and readonly mode with scroll support)
|
||||||
* 
|
* 
|
||||||
|
* LoginDialog - a simple authorization dialog with two fields: Username and Password
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
The main demo (theme changing and radio group control)
|
The main demo (theme changing and radio group control)
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
2018-08-13 - version 0.9.0 RC2
|
||||||
|
[+] New control: LoginDialog (a dialog with username and password fields)
|
||||||
|
|
||||||
2018-08-04 - version 0.9.0 RC1
|
2018-08-04 - version 0.9.0 RC1
|
||||||
[+] New control: File Picker (a dialog for file save/load operations)
|
[+] New control: File Picker (a dialog for file save/load operations)
|
||||||
[+] New property for Label: TextDisplay. It defines which part of Label title
|
[+] New property for Label: TextDisplay. It defines which part of Label title
|
||||||
|
92
demos/logindlg/logindlg.go
Normal file
92
demos/logindlg/logindlg.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
ui "github.com/VladimirMarkelov/clui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createView() {
|
||||||
|
view := ui.AddWindow(0, 0, 30, 7, "Login dialog")
|
||||||
|
view.SetPack(ui.Vertical)
|
||||||
|
view.SetGaps(0, 1)
|
||||||
|
view.SetPaddings(2, 2)
|
||||||
|
|
||||||
|
frmOpts := ui.CreateFrame(view, 1, 1, ui.BorderNone, ui.Fixed)
|
||||||
|
frmOpts.SetPack(ui.Horizontal)
|
||||||
|
cbCheck := ui.CreateCheckBox(frmOpts, ui.AutoSize, "Use callback to test data", ui.Fixed)
|
||||||
|
|
||||||
|
ui.CreateLabel(view, ui.AutoSize, ui.AutoSize, "Correct credentials", ui.Fixed)
|
||||||
|
|
||||||
|
frmCreds := ui.CreateFrame(view, 1, 1, ui.BorderNone, ui.Fixed)
|
||||||
|
frmCreds.SetPack(ui.Horizontal)
|
||||||
|
frmCreds.SetGaps(1, 0)
|
||||||
|
ui.CreateLabel(frmCreds, ui.AutoSize, ui.AutoSize, "Username", ui.Fixed)
|
||||||
|
edUser := ui.CreateEditField(frmCreds, 8, "", 1)
|
||||||
|
ui.CreateLabel(frmCreds, ui.AutoSize, ui.AutoSize, "Password", ui.Fixed)
|
||||||
|
edPass := ui.CreateEditField(frmCreds, 8, "", 1)
|
||||||
|
|
||||||
|
lbRes := ui.CreateLabel(view, ui.AutoSize, ui.AutoSize, "Result:", ui.Fixed)
|
||||||
|
|
||||||
|
frmBtns := ui.CreateFrame(view, 1, 1, ui.BorderNone, ui.Fixed)
|
||||||
|
frmBtns.SetPack(ui.Horizontal)
|
||||||
|
btnDlg := ui.CreateButton(frmBtns, ui.AutoSize, 4, "Login", ui.Fixed)
|
||||||
|
btnQuit := ui.CreateButton(frmBtns, ui.AutoSize, 4, "Quit", ui.Fixed)
|
||||||
|
ui.CreateFrame(frmBtns, 1, 1, ui.BorderNone, 1)
|
||||||
|
|
||||||
|
ui.ActivateControl(view, edUser)
|
||||||
|
|
||||||
|
btnDlg.OnClick(func(ev ui.Event) {
|
||||||
|
dlg := ui.CreateLoginDialog(
|
||||||
|
"Enter credentials",
|
||||||
|
edUser.Title(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if cbCheck.State() == 1 {
|
||||||
|
dlg.OnCheck(func(u, p string) bool {
|
||||||
|
return u == edUser.Title() && p == edPass.Title()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
dlg.OnCheck(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
dlg.OnClose(func() {
|
||||||
|
if dlg.Action == ui.LoginCanceled {
|
||||||
|
lbRes.SetTitle("Result:\nDialog canceled")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dlg.Action == ui.LoginInvalid {
|
||||||
|
lbRes.SetTitle("Result:\nInvalid username or password")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dlg.Action == ui.LoginOk {
|
||||||
|
if cbCheck.State() == 1 {
|
||||||
|
lbRes.SetTitle("Result:\nLogged in successfully")
|
||||||
|
} else {
|
||||||
|
lbRes.SetTitle("Result:\nEntered [" + dlg.Username + ":" + dlg.Password + "]")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
btnQuit.OnClick(func(ev ui.Event) {
|
||||||
|
go ui.Stop()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func mainLoop() {
|
||||||
|
// Every application must create a single Composer and
|
||||||
|
// call its intialize method
|
||||||
|
ui.InitLibrary()
|
||||||
|
defer ui.DeinitLibrary()
|
||||||
|
|
||||||
|
createView()
|
||||||
|
|
||||||
|
// start event processing loop - the main core of the library
|
||||||
|
ui.MainLoop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mainLoop()
|
||||||
|
}
|
159
logindlg.go
Normal file
159
logindlg.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package clui
|
||||||
|
|
||||||
|
const (
|
||||||
|
LoginOk = iota
|
||||||
|
LoginCanceled
|
||||||
|
LoginInvalid
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoginDialog is a login dialog with fields to enter user name and password
|
||||||
|
// Public properties:
|
||||||
|
// * Username - login entered by a user
|
||||||
|
// * Password - password entered by a user
|
||||||
|
// * Action - how the dialog was closed:
|
||||||
|
// - LoginOk - button "OK" was clicked
|
||||||
|
// - LoginCanceled - button "Cancel" was clicked or dialog was dismissed
|
||||||
|
// - LoginInvalid - invalid credentials were entered. This value appears
|
||||||
|
// only in case of callback is used and button "OK" is clicked
|
||||||
|
// while entered username or password is incorrect
|
||||||
|
type LoginDialog struct {
|
||||||
|
View *Window
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Action int
|
||||||
|
|
||||||
|
result int
|
||||||
|
onClose func()
|
||||||
|
onCheck func(string, string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginDialog creates a new login dialog
|
||||||
|
// * title - custom dialog title
|
||||||
|
// * userName - initial username. Maybe useful if you want to implement
|
||||||
|
// a feature "remember me"
|
||||||
|
// The active control depends on userName: if it is empty then the cursor is
|
||||||
|
// in Username field, and in Password field otherwise.
|
||||||
|
// By default the dialog is closed when button "OK" is clicked. But if you set
|
||||||
|
// OnCheck callback the dialog closes only if callback returns true or
|
||||||
|
// button "Cancel" is clicked. This is helpful if you do not want to recreate
|
||||||
|
// the dialog after every incorrect credentials. So, you define a callback
|
||||||
|
// that checks whether pair of Usename and Password is correct and then the
|
||||||
|
// button "OK" closed the dialog only if the callback returns true. If the
|
||||||
|
// credentials are not valid, then the dialog shows a warning. The warning
|
||||||
|
// automatically disappears when a user starts typing in Password or Username
|
||||||
|
// field.
|
||||||
|
func CreateLoginDialog(title, userName string) *LoginDialog {
|
||||||
|
dlg := new(LoginDialog)
|
||||||
|
|
||||||
|
dlg.View = AddWindow(15, 8, 10, 4, title)
|
||||||
|
WindowManager().BeginUpdate()
|
||||||
|
defer WindowManager().EndUpdate()
|
||||||
|
|
||||||
|
dlg.View.SetModal(true)
|
||||||
|
dlg.View.SetPack(Vertical)
|
||||||
|
|
||||||
|
userfrm := CreateFrame(dlg.View, 1, 1, BorderNone, Fixed)
|
||||||
|
userfrm.SetPaddings(1, 1)
|
||||||
|
userfrm.SetPack(Horizontal)
|
||||||
|
userfrm.SetGaps(1, 0)
|
||||||
|
CreateLabel(userfrm, AutoSize, AutoSize, "User name", Fixed)
|
||||||
|
edUser := CreateEditField(userfrm, 20, userName, 1)
|
||||||
|
|
||||||
|
passfrm := CreateFrame(dlg.View, 1, 1, BorderNone, 1)
|
||||||
|
passfrm.SetPaddings(1, 1)
|
||||||
|
passfrm.SetPack(Horizontal)
|
||||||
|
passfrm.SetGaps(1, 0)
|
||||||
|
CreateLabel(passfrm, AutoSize, AutoSize, "Password", Fixed)
|
||||||
|
edPass := CreateEditField(passfrm, 20, "", 1)
|
||||||
|
edPass.SetPasswordMode(true)
|
||||||
|
|
||||||
|
filler := CreateFrame(dlg.View, 1, 1, BorderNone, 1)
|
||||||
|
filler.SetPack(Horizontal)
|
||||||
|
lbRes := CreateLabel(filler, AutoSize, AutoSize, "", 1)
|
||||||
|
|
||||||
|
blist := CreateFrame(dlg.View, 1, 1, BorderNone, Fixed)
|
||||||
|
blist.SetPack(Horizontal)
|
||||||
|
blist.SetPaddings(1, 1)
|
||||||
|
btnOk := CreateButton(blist, 10, 4, "OK", Fixed)
|
||||||
|
btnCancel := CreateButton(blist, 10, 4, "Cancel", Fixed)
|
||||||
|
|
||||||
|
btnCancel.OnClick(func(ev Event) {
|
||||||
|
WindowManager().DestroyWindow(dlg.View)
|
||||||
|
WindowManager().BeginUpdate()
|
||||||
|
dlg.Action = LoginCanceled
|
||||||
|
closeFunc := dlg.onClose
|
||||||
|
WindowManager().EndUpdate()
|
||||||
|
if closeFunc != nil {
|
||||||
|
closeFunc()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
btnOk.OnClick(func(ev Event) {
|
||||||
|
if dlg.onCheck != nil && !dlg.onCheck(edUser.Title(), edPass.Title()) {
|
||||||
|
lbRes.SetTitle("Invalid username or password")
|
||||||
|
dlg.Action = LoginInvalid
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dlg.Action = LoginOk
|
||||||
|
if dlg.onCheck == nil {
|
||||||
|
dlg.Username = edUser.Title()
|
||||||
|
dlg.Password = edPass.Title()
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowManager().DestroyWindow(dlg.View)
|
||||||
|
WindowManager().BeginUpdate()
|
||||||
|
|
||||||
|
closeFunc := dlg.onClose
|
||||||
|
WindowManager().EndUpdate()
|
||||||
|
if closeFunc != nil {
|
||||||
|
closeFunc()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dlg.View.OnClose(func(ev Event) bool {
|
||||||
|
if dlg.result == DialogAlive {
|
||||||
|
dlg.result = DialogClosed
|
||||||
|
if ev.X != 1 {
|
||||||
|
WindowManager().DestroyWindow(dlg.View)
|
||||||
|
}
|
||||||
|
if dlg.onClose != nil {
|
||||||
|
dlg.onClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
edUser.OnChange(func(ev Event) {
|
||||||
|
lbRes.SetTitle("")
|
||||||
|
})
|
||||||
|
edPass.OnChange(func(ev Event) {
|
||||||
|
lbRes.SetTitle("")
|
||||||
|
})
|
||||||
|
|
||||||
|
if userName == "" {
|
||||||
|
ActivateControl(dlg.View, edUser)
|
||||||
|
} else {
|
||||||
|
ActivateControl(dlg.View, edPass)
|
||||||
|
}
|
||||||
|
return dlg
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnClose sets the callback that is called when the
|
||||||
|
// dialog is closed
|
||||||
|
func (d *LoginDialog) OnClose(fn func()) {
|
||||||
|
WindowManager().BeginUpdate()
|
||||||
|
defer WindowManager().EndUpdate()
|
||||||
|
d.onClose = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnCheck sets the callback that is called when the
|
||||||
|
// button "OK" is clicked. The dialog sends to the callback two arguments:
|
||||||
|
// username and password. The callback validates the arguments and if
|
||||||
|
// the credentials are valid it returns true. That means the dialog can be
|
||||||
|
// closed. If the callback returns false then the dialog remains on the screen.
|
||||||
|
func (d *LoginDialog) OnCheck(fn func(string, string) bool) {
|
||||||
|
WindowManager().BeginUpdate()
|
||||||
|
defer WindowManager().EndUpdate()
|
||||||
|
d.onCheck = fn
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user