-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bca28ea
commit 9a5b434
Showing
4 changed files
with
256 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |