unigui

package module
v0.0.0-...-69a3bff Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2023 License: MIT Imports: 14 Imported by: 0

README

unigui-go

Universal GUI Framework and Protocol (Go)

Purpose

Provide a smart programming technology that does not require front-end programming, for a server written in any language, for displaying on any device, in any resolution, without any tuning.

Import
import . "github.com/Claus1/unigui-go"
How does work inside

The exchange protocol for the solution is JSON as the most universally accessible, comprehensible, readable, and popular format compatible with all programming languages. The server sends JSON data to Unigui which has built-in tools (autodesigner) and automatically builds a standart Google Material Design GUI for user data. No markup, drawing instructions and the other dull job are required. Just the simplest description what you want. From the constructed Unigui screen the server receives a JSON message flow which fully describes what the user did. The message format is ["Block", "Elem", "type of action", value], where "Block"and "Elem"are the names of the block and its element, "value" is the JSON value of the action/event that has happened. The server can either accept the change or roll it back by sending an info window about an inconsistency. The server can open a dialog box, send popup Warning, Error,.. or an entirely new screen. Unigui instantly and automatically displays actual server state.

Programming

Unigui is the language and platform independent technology. This repo explains how to work with Unigui using Go and the tiny but optimal framework for that. Unigui web version is included in this library. Unigui for Python is accessible in https://github.com/Claus1/unigui

High level - Screen

The program declares screen builder function which has to be registered.

Screen example examples/HelloUnigui.go The block example with a table, button and selector

package main

import . "github.com/Claus1/unigui-go"

func screenTest(user* User)* Screen_{	
	table := Table("Videos",0, nil, []string{"Video", "Duration",  "Links", "Mine"},
	    SeqSeq(Seq("opt_sync1_3_0.mp4", "30 seconds",  "@Refer to signal1", true),
		       Seq("opt_sync1_3_0.mp4", "37 seconds",  "@Refer to signal8", false)))
			
	cleanButton := Button("Clean table", nil, "")
	selector := Select("Select", "All", nil, []string{"All","Based","Group"})
	block := Block("X Block", Seq(cleanButton, selector), table)
	block.Icon = "api"
	return Screen(block)	
}
func main(){			
	//register screens
	Register(screenTest, "Main", 0, "api")	
	Start()
}
Server start

Unigui builds the interactive app for the code above. Connect a browser to localhast:8080 and will see:

image

Possible to define ports and upload directory in config file. By default they are

port = :8000
appname = "Test app"
Handling events

All handlers are functions which have a signature

func handlerX(valueX any) any

where valueX is a value for the event.

All Gui objects have a field ‘Value’. For an edit field the value is a string or number, for a switch or check button the value is boolean, for table is row id, e.t.c. When a user changes the value of the Gui object or presses Button, the server calls the ‘Changed’ function handler.

cleanTable := func(v Any) Any{

	table.Rows = SeqSeq() //empty [][]Any    
	return table          //table is changed, just return it for updating on the screen
}
cleanButton := Button("Clean table", cleanTable, "")

where Any, Seq, SeqSeq just short names defined as

type Any = interface{}
func Seq(arr ...Any) []Any{
	return arr
}
func SeqSeq(arr ...[]Any) [][]Any{
	return arr
}

‘Changed’ handlers have to return Gui object or array(Seq) of Gui objects that were changed by the handler and Unigui has to redraw or does nothing if all visible elements have the same state. Unigui will do all other jobs for synchronizing automatically. Gui objects out of box have "Changed" handlers which accepts incoming value automatically to the "Value" variable of gui object. Can be redefined by the user.

If "Value" is not acceptable instead of returning an object possible to return Info, Error or Warning or UpdateError pop-up window. The last function has an object parameter, which has to be synchronized simultaneously with informing about the Error.

selector := Select("Select", "All", nil, []string{"All","Based","Group"})

selector.Changed = func(v Any) Any{
    if v == "Based"{
        return UpdateError(selector, "Select can not be Based!")
    }
	selector.Value = v
    return nil
})	
If a handler returns true the whole screen will be redrawn. Also it causes calling Screen function Prepare() which used for syncronizing GUI elements one to another and with the program/system data. Prepare() is also automatically called when the screen loaded. Prepare() is optional.

If the handler returns nil Unigui considers it as Ok and does nothing.

Block details

The width and height of blocks is calculated automatically depending on their children. It is possible to set the block width and make it scrollable in height, for example for images list. Possible to add MD icon to the header, if required. Width, Scroll, Height .. are optional. Block helper is

func Block(name string, topChilds []Any, childs ...Any) *Block_ 

The topChilds parameter of the Block constructor is an array of widgets which has to be in the header just after the block name. Blocks can be shared between the user screens with its states. Such a block has to be declared inside Block making function and registered by call ShareBlock(myBlock). Examples of such block examples/shared_block.go:


func sharedAudios(user* User) Any{
	table := Table("Audios",0, nil, []string{"Audio", "Duration,sec", "Stars"}, genRows())
	tableBlock := Block("Table chart, Button("Press me", nil), table)
	tableBlock.Icon = "insights"
	return tableBlock
}
func main(){		
	//register shared blocks
	ShareBlock(sharedAudios, "Audios")
	...
	//register screens
	...
}

Block making function returns Any because it is possible to return a block combination, which has []Any type. See example such combination in examples/Test blocks/shared_block.go

If child elements are enumerated inside an array, the block will display them on a line, otherwise everyone will be displayed on a new own line(s).

Using a shared block in some screen:

scr :=  Screen(Seq(block, bottomBlock), user.SharedBlock("Audios"))	//user is always accessible in a screen making function

Events interception of shared blocks

Interception handlers have the same in/out format as usual handlers.

They overrides inner element handler call. If such method returns false the inner element handler will be calld.

For example above the interception of select_mode changed event will be:

screen.Handle(elemName string, blockName string, handlerName string, func(v Any) Any{
    if v == "Based"{
        return UpdateError(selector, "Select can not be Based!")
    }
    return false //call the inner element handlers.
})	
Layout of blocks.

If the blocks are simply listed Unigui draws them from left to right or from top to bottom depending on the orientation setting. If a different layout is needed, it can be set according to the following rule: if the vertical area must contain more than one block, then the enumeration in the array will arrange the elements vertically one after another. If such an element enumeration is an array of blocks, then they will be drawn horizontally in the corresponding area.

Example

screen = Screen(Seq(b1,b2), Seq(b3, Seq(b4, b5))) #[b1,b2] - the first vertical area, [b3, [b4, b5]] - the second one.

image

Basic gui elements
If the element name starts with _ , Unigui will hide its name.

if we need to paint an icon in the element, set the element "Icon" to "any MD icon name".

Button

Normal button.

Button(name string,push_callback Handler, icon string) 

Icon button : name is started form _ for hiding

Button("_Check", push_callback, "check") //icon == "check" in MD icon list
Load to server Button

Special button provides file uploading from user device or computer to the Unigui server.

UploadButton(name string, handler_when_loading_finish Handler, icon string)

handler_when_loading_finish(the_loaded_file_filename) where the_loaded_file_filename is a file name in upload server folder. This folder name is global UploadDir parameter in unigui which can be changed before Start().

Camera Button

Special button provides to make a photo on the user mobile device.

CameraButton("Make a photo", handler_when_shooting_finish)

handler_when_loading_finish(button_, name_of_loaded_file) where name_of_loaded_file is the made photo name in the server folder. This folder name is global UploadDir parameter.

Edit and Text field.
Edit(name string, value Any, changed_handler) 
numEdit := Edit("Number field", 0.9, nil) #for edit number

If set Edit = false it will be readonly field.

numEdit.Edit = false

//Text field
Text("Some text")

Complete handler is optional function which accepts the current field value and returns a string list for autocomplete.

edit := Edit("Edit me",  "")
edit.Complete = getCompleteList 

def getCompleteList(current_value Any):
    return []string{"option1","option2","option3"}    

Possible to set "Update" handler which is called when the user press Enter in the field. It can return nil or Gui object(s) for updating as usual handler.

Radio button
Switch("Radio button", value bool, changed Handler) #changed - optional
Select group. Contains options field.
//build select field
Select(name String, value Any, selectionChanged Handler, options []string)

//build as vertical list
List(name String, value Any, selectionChanged Handler, options []string)

Select can be such type "toggles","dropdown". Unigui automatically chooses between toogles and dropdown, but the user can set preferrable type then Unigui build it as the user want.

Image.

width, and height are optional wh, click is called if the user click or touch the image, can be nil as all Hadlers

func Image(name string, image string, click Handler, wh ...int) *Image_

Tree. The element for tree-like data.
func Tree(name string, value string, selected Handler, fields *map[string]string) *Tree_

fields for the tree data {item_name:parent_name}.

parent_name is "" for root items. selected is called when the user clicks(selects) on a tree item.

Table.

Tables is common structure for presenting 2D data and charts. Can contain Append, Delete, Update handlers, Multimode parameter is True if allowed single and multi select mode. True by default. All of them are optional. When you assign a handler for such action Unigui will draw the appropriate action icon button in the table header automatically. If Modify and Update are not defined, unigui will not draw Edit button and user can not edit the table data. The same rule for Delete, Append, e.t.c.

func Table(name string, value Any, selected Handler, headers []string, rows [][]Any) *Table_

If "headers" length is equal "rows" length Unigui counts rows id as an index in rows array. If "rows" length is "headers" length + 1, Unigui counts rows id as the last row field. So it is possible to use some keys as row ids just by adding them to the rows as the last element. value == Seq(0) means 0 row is selected in multiselect mode (in array). value = 1 means rows at 1 index is selected in sinlge mode selection.

By default Table has toolbar with search field and icon action buttons. It is possible to hide it if set "Tools" table variable to false.

Table shows a paginator if all rows can not be drawn on the screen. Otherwise the table paginator is redundant and omitted.

If the selected row is not on the currently visible page then setting "Show" = true table parameter causes Unigui to switch to the page with the selected row.

Table handlers.

Complete, Modify and Update are CellHandlers where CellHandler = func(cellValue TableCell) Any . cellValue is consisted from the cell value and its position in the table.

TableCell struct {
	Value Any
	Where [2]int
}

"Update" is called when the user presses the Enter, "Modify" when the cell value is changed by the user. By default it has standart modify method which updates rows data, it can be locked by setting the table variable Modify to nil or Edit to false. They can return Error(..) or Warning(..) if the value is not accepted, othewise return false for accepting the value (false means continue the standart process).

table.Update = func(tvalue TableCell) Any{
    AcceptRowValue(table, &tvalue)
    return Warning(F("%v is updated to %v",table.Name, tvalue.Value))
}

The "Changed" table handler accepts the selected row number or id as a value.

"Editing" handler is called when the user switches the table edit mode. it is optional and has standart signature where the parameter says the table is being edited or not.

Chart

Chart is a table with additional Table parameter "View" which explaines unigui how to draw a chart. The format is "{x index}-{y index1},{y index2}[,..]". "0-1,2,3" means that x axis values will be taken from 0 column, and y values from 1,2,3 columns of row data. "i-3,5" means that x axis values will be equal the row indexes in rows, and y values from 3,5 columns of rows data. If the user set View parameter then unigui displays a chart icon at the table header, pushing that switches table mode to the chart mode. If a table constructor set Type to "linechart" in addition to "View" parameter the table will be displayed as a chart on start. In the chart mode pushing the icon button on the top right switches back to the table row mode. Chart example in examples/Test blocks/shared_block.go

Dialog
func Dialog(name string, text string, callback Handler, buttons ...string) *Dialog_

where buttons is a list of the dialog buttons like "Yes","No", "Cancel". Dialog callback has the signature as other with value == pushed button name

func dialogCallback(pressedButton Any) Any{
  return Warning(F("The user pressed the button %v", pressedButton))
}

Content dialog field can be filled by any Block for additional dialog functionality.

Popup windows

They are intended for non-blocking displaying of error messages and informing about some events, for example, incorrect user input or completion of a long process on the server.

Info(info_message)
Warning(warning_message)
Error(error_message)

They are returned by handlers and cause appearing on the top screen colored rectangles window for 3 second. UpdateError also says Unigui to update updated_element.

For long time processes it is possible to create Progress window. It is just call user.Progress("some message"). Open window user.progress("Analyze .. Wait..") Update window message user.progress(" 1% is done..") Close window user.progress(nil) or automatically when the handler returns something.

Other subtle benefits of a Unigui protocol and technology.
  1. Possible to works with any set of resource process servers as a single system, within the same GUI user space, carries out any available operations, including cross, on the fly.
  2. Reproduces and saves sequences of the user interaction with the system without programming. It can be used for complex testing, supporting of security protocols and more.
  3. Possible to mirror a session to other users, works simultaneously in one session for many users.
Milti-user programming? You dont need it!

Unigui automatically creates and serves an environment for every user. The management class is User which contains all required methods for processing and handling the user activity. A programmer can assign methods

Save, Back, Forward, Undo, Redo func(User) Any
//also store and use any data in User.Extention which is defined as 
Extension  map[string]Any

Such methods suit for history navigation, undo/redo and initial operations.

For constructing custom User use UserConstuctor variable which return new User for any new session.

UserConstuctor = func() *User{
    user := User
	//standart conctructor
	user.Init()
    //assign custom function
    ..
    return &user
}

By default Unigui UserConstuctor creates a user with empty behavior functions and Extension fields.

In screen and shared block functions User is automatically acccesible as the function argument.

More info about User class and methods you can find in manager.go in the root dir.

Examples are in examples folder.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ResourcePort = ":8000"
	WsocketPort  = ":8000"
	UploadDir    = "upload"
	SocketIp     = "localhost"
	AppName      = "Unigui"
)
View Source
var (
	F     = fmt.Sprintf
	Print = fmt.Println
)
View Source
var UserConstructor = func(cl *Client) *User {
	user := User{}
	user.Init(cl)
	return &user
}

Functions

func AcceptRowValue

func AcceptRowValue(t *Table_, tc *TableCell)

func Fname2url

func Fname2url(fn string) string

func GetConfig

func GetConfig(param string) string

func ReadConfig

func ReadConfig()

func Register

func Register(sgen screenGen, scrName string, order int, icon string)

func RemoveIndex

func RemoveIndex(s []string, index int) []string

func SeqSeq

func SeqSeq(arr ...[]Any) [][]Any

func ShareBlock

func ShareBlock(bg blockGen, name string)

func Start

func Start()

func ToInt

func ToInt(val Any) int

func ToJson

func ToJson(o Any) []byte

func ToString

func ToString(v Any) string

func UploadFname

func UploadFname(shortName Any) string

func Url2fname

func Url2fname(url string) string

Types

type Answer

type Answer struct {
	Answer Any `json:"answer"`
	Param  Any `json:"param"`
	Id     int `json:"id"`
}

type Any

type Any = interface{}

func Seq

func Seq(arr ...Any) []Any

type Block_

type Block_ struct {
	Name, Icon         string
	Top_childs, Childs []Any
	Scroll             bool
	Width, Height      int
}

func Block

func Block(name string, top_childs []Any, childs ...Any) *Block_

func (Block_) MarshalJSON

func (s Block_) MarshalJSON() ([]byte, error)

type CellHandler

type CellHandler = func(cellValue TableCell) Any

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a middleman between the websocket connection and the hub.

type Dialog_

type Dialog_ struct {
	Name, Text, Type string
	Content          *Block_
	Buttons          []string
	Callback         Handler
}

func Dialog

func Dialog(name string, text string, callback Handler, buttons ...string) *Dialog_

func (Dialog_) MarshalJSON

func (s Dialog_) MarshalJSON() ([]byte, error)

type Edit_

type Edit_ struct {
	Gui
	Complete, Update Handler
	Edit             bool
}

func Edit

func Edit(name string, value Any, changed Handler) *Edit_

func Text

func Text(str string) *Edit_

func (Edit_) MarshalJSON

func (s Edit_) MarshalJSON() ([]byte, error)

type Gui

type Gui struct {
	Name    string
	Value   Any
	Changed Handler
	Type    string
	Icon    string
}

func Button

func Button(name string, clicked Handler, icon string) *Gui

func CameraButton

func CameraButton(name string, clicked Handler, icon string) *Gui

func Switch

func Switch(name string, value bool, changed Handler) *Gui

func UploadButton

func UploadButton(name string, clicked Handler, icon string) *Gui

func (Gui) MarshalJSON

func (s Gui) MarshalJSON() ([]byte, error)

type Handler

type Handler = func(value Any) Any

type Hub

type Hub struct {
	// contains filtered or unexported fields
}

Hub maintains the set of active clients and sends messages to the clients.

type IGui

type IGui interface {
	// contains filtered or unexported methods
}

type Image_

type Image_ struct {
	Gui
	Image  string
	Scroll bool
	Width  int
	Height int
}

func Image

func Image(name string, image string, click Handler, wh ...int) *Image_

func (Image_) MarshalJSON

func (s Image_) MarshalJSON() ([]byte, error)

type Oper

type Oper interface {
	// contains filtered or unexported methods
}

type Popwindow

type Popwindow struct {
	Error, Warning, Info, Progress string
	Data, Update                   Any
}

func Error

func Error(str string) *Popwindow

func Info

func Info(str string) *Popwindow

func UpdateError

func UpdateError(elem2update Any, str string) *Popwindow

func Warning

func Warning(str string) *Popwindow

func (Popwindow) MarshalJSON

func (s Popwindow) MarshalJSON() ([]byte, error)

type Screen_

type Screen_ struct {
	Name, Icon, Header, Type string
	Blocks                   []Any
	Order                    int
	Prepare, Save            func()
	Toolbar                  []*Gui
	// contains filtered or unexported fields
}

func Screen

func Screen(blocks ...Any) *Screen_

func (*Screen_) Handle

func (s *Screen_) Handle(elemName string, blockName string, nameFunc string, handler Handler)

func (Screen_) MarshalJSON

func (s Screen_) MarshalJSON() ([]byte, error)

type Select_

type Select_ struct {
	Gui
	Options []string
}

func List

func List(name string, value Any, changed Handler, options []string) *Select_

func Select

func Select(name string, value Any, changed Handler, options []string) *Select_

func (Select_) MarshalJSON

func (s Select_) MarshalJSON() ([]byte, error)

type Signal

type Signal struct {
	Maker IGui
	Value string
}

type TableCell

type TableCell struct {
	Value Any
	Where [2]int
}

type Table_

type Table_ struct {
	Gui
	View    string
	Headers []string
	Rows    [][]Any

	//Update the user pressed Enter in the field
	Complete, Modify, Update CellHandler
	Append, Delete           Handler //row
	Multimode                bool
	Edit                     bool
	//called when edit mode is changed by the user
	Editing Handler
	//setting to true causes switching to the page with selected row
	Show bool
	//if fasle the table tools are not visible
	Tools bool
}

func Table

func Table(name string, value Any, selected Handler, headers []string, rows [][]Any) *Table_

func (Table_) MarshalJSON

func (s Table_) MarshalJSON() ([]byte, error)

type Tree_

type Tree_ struct {
	Gui
	Options map[string]string
}

func Tree

func Tree(name string, value string, selected Handler, fields map[string]string) *Tree_

func (Tree_) MarshalJSON

func (s Tree_) MarshalJSON() ([]byte, error)

type Updater

type Updater struct {
	Update, Data Any
	Multi        bool
}

func (Updater) MarshalJSON

func (s Updater) MarshalJSON() ([]byte, error)

type User

type User struct {
	UndoBuffer, RedoBuffer, History []Oper
	HistoryCurrent                  int

	Toolbar                         []*Gui
	Save, Back, Forward, Undo, Redo func(*User) Any
	Extension                       map[string]Any
	// contains filtered or unexported fields
}

func (*User) Init

func (user *User) Init(cl *Client)

func (*User) Progress

func (u *User) Progress(str string)

func (*User) SetScreen

func (user *User) SetScreen(name string) bool

func (*User) SharedBlock

func (user *User) SharedBlock(name string) Any

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL