Simple Documnet Based Appliaction With Sciter and Golang

document base application with golang and sciter
document base application with golang and sciter


You might need to create a document based application with Sciter. Here the main question is how to get input from the user, how to store it during processing. Then how to store it to a file ( with existing or your own extension ), and at the end how to reopen that file again for editing or updating it.

Here I am going with a very simple example of a document-based application. Notepad-plus-minus ( no rights reserved 😀 )It'sts a very simple notepad even simple then notepad. You can think of as a sticky note which allows you to save it as a file.


Demo First






GUI File


In gui we need a menubar [ created with CSS obviously ] which provided options for open, save, exit and new. It also has to have an input area where we can write. So let's get started with creating menu first.

<html>
    <style>
        .top-menu{
            position: absolute;
            width: 300dip;
            top: 0;
            left: 0;
            padding: 0;
            margin: 0;
        }
        ul.menubar{
            width:  300dip;
            background-color : #cccccc;            
            padding: 0  4px;   
            margin: 0;        
        }
        ul.menubar > li {
            display: inline-block;
            color: #666666;
            padding: 0 5px;   
            margin: 0 5px;   
            position: relative;
        }
        ul.menubar > li:hover {
            color: #cccccc;
            background: #666666;                         
        }  
        ul.sub-menu {
            position: absolute;
            display: none;
            top: 16dip;
            left: 0;
            width: auto;
            height: auto;
            list-style: none;
            margin: 0;
            padding: 0;
            box-shadow:  2dip 2dip 2dip rgba(100,100,100,0.6)
        }     
        ul.sub-menu > li {
            padding: 2dip;
            border-bottom:  2px dotted #cccccc;
            color: #333333;
        }
        ul.menubar > li:hover > ul.sub-menu {                          
            display:block;
            background: #fff;           
        }    
    </style>
    <body>          
        <!-- Simulating Menubar for notepad -->
        <div class="top-menu">            
            <ul class="menubar">
                <li>
                    File
                    <ul class="sub-menu">
                        <li #new>New</li>
                        <li #open>Open</li>
                        <li #save>Save</li>
                        <li #exit>Exit</li>
                    </ul>
                </li>                
            </ul>
        </div>  
        ...
    </body>
</html>

We are using position absolute. Because want every element to start from some fixed position to some fixed position, and for making it simple we are going to use fixed sized window so we don't have to tackle the responsivness issue for now.
In upper code, we have just created menubar which sticks to the top of windows. I don't think to have to explain that pretty simple CSS. But if you need just comment out.

<html>
    <style>
        .top-menu{
            position: absolute;
            width: 300dip;
            top: 0;
            left: 0;
            padding: 0;
            margin: 0;
        }
        ul.menubar{
            width:  300dip;
            background-color : #cccccc;            
            padding: 0  4px;   
            margin: 0;        
        }
        ul.menubar > li {
            display: inline-block;
            color: #666666;
            padding: 0 5px;   
            margin: 0 5px;   
            position: relative;
        }
        ul.menubar > li:hover {
            color: #cccccc;
            background: #666666;                         
        }
        ul.sub-menu {
            position: absolute;
            display: none;
            top: 16dip;
            left: 0;
            width: auto;
            height: auto;
            list-style: none;
            margin: 0;
            padding: 0;
            box-shadow:  2dip 2dip 2dip rgba(100,100,100,0.6)
        }     
        ul.sub-menu > li {
            padding: 2dip;
            border-bottom:  2px dotted #cccccc;
            color: #333333;
        }
        ul.menubar > li:hover > ul.sub-menu {                          
            display:block;
            background: #fff;           
        }     
        textarea{  
            position: absolute;
            top:16dip;
            left:0dip;      
            width: 295dip;      
            font-size: 14dip;     
            background:antiquewhite;
            border: none;           
            overflow-y: auto;
        }
    </style>
    <body>       
        
        <!-- Textarea to get input from user-->
        <textarea cols="48" rows="24" #data></textarea>        

        <!-- Simulating Menubar for notepad -->
        <div class="top-menu">            
            <ul class="menubar">
                <li>
                    File
                    <ul class="sub-menu">
                        <li #new>New</li>
                        <li #open>Open</li>
                        <li #save>Save</li>
                        <li #exit>Exit</li>
                    </ul>
                </li>                
            </ul>
        </div>        
    </body>
</html>

We have added a textarea here which will be our page where we will be writing. As you can see I have added textarea before the menu. I don't know why but when we use position absolute sciter renders the element in order of the declaration on dom. It does not follow the z-index. ( or It may be some of my fault I don't find. If I will be able to find, I will update this for sure )

Cool, We have made our UI Ready
What we need is now TIScript to do desired operations on like open and save file, create a blank (new) file and close application.

<html>
    <style>
        .top-menu{
            position: absolute;
            width: 300dip;
            top: 0;
            left: 0;
            padding: 0;
            margin: 0;
        }
        ul.menubar{
            width:  300dip;
            background-color : #cccccc;            
            padding: 0  4px;   
            margin: 0;        
        }
        ul.menubar > li {
            display: inline-block;
            color: #666666;
            padding: 0 5px;   
            margin: 0 5px;   
            position: relative;
        }
        ul.menubar > li:hover {
            color: #cccccc;
            background: #666666;                         
        }    

        ul.sub-menu {
            position: absolute;
            display: none;
            top: 16dip;
            left: 0;
            width: auto;
            height: auto;
            list-style: none;
            margin: 0;
            padding: 0;
            box-shadow:  2dip 2dip 2dip rgba(100,100,100,0.6)
        }     
        ul.sub-menu > li {
            padding: 2dip;
            border-bottom:  2px dotted #cccccc;
            color: #333333;
        }
        ul.menubar > li:hover > ul.sub-menu {                          
            display:block;
            background: #fff;           
        }     
        textarea{  
            position: absolute;
            top:16dip;
            left:0dip;      
            width: 295dip;      
            font-size: 14dip;     
            background:antiquewhite;
            border: none;           
            overflow-y: auto;
        }
    </style>
    <body>       
        
        <!-- Textarea to get input from user-->
        <textarea cols="48" rows="24" #data></textarea>        

        <!-- Simulating Menubar for notepad -->
        <div class="top-menu">            
            <ul class="menubar">
                <li>
                    File
                    <ul class="sub-menu">
                        <li #new>New</li>
                        <li #open>Open</li>
                        <li #save>Save</li>
                        <li #exit>Exit</li>
                    </ul>
                </li>                
            </ul>
        </div>   

        <script type="text/tiscript">
        
            event click $(#exit){
                view.closeWindow()
            }
        
            event click $(#open){
                const HTML_FILES = "Notepad Plus Minus (*.nppm)|*.nppm";    
                var filepath = view.selectFile(#open, HTML_FILES 
                   , "Unnamed", "file", "File name to save:");
                // incase of  file was not selected then
                // function returns null
                if (filepath){
                    self#data.text = view.open(filepath)  
                }
            }

            event click $(#save){
                const HTML_FILES = "Notepad Plus Minus (*.nppm)|*.nppm";
                var filepath = view.selectFile(#save, HTML_FILES 
                 , "Unnamed", "file", "File name to save:");
                view.save(filepath,self#data.text)           
            }
        
            event click $(#new){
                self#data.text = ""
            }
        
        </script>

    </body>
</html>

Here seletFile is a special function which generates dialogue box for opening and saving file[ it depends on first argument | #open or #save ]. When the function returns complete path to a file [ If the file was selected ] or null [ If the function was cancelled ]. Otherwise, there is nothing new in this code. We are just calling golang function open in case of open and assigning data what we are receiving from golang. and in case of saving we are passing data of textarea to golang as arguments for saving the file using golang save function.

GoLang File

main.go
package main

import (
"fmt"
"io/ioutil"
"os"
"strings"
"syscall"
"github.com/sciter-sdk/go-sciter"
"github.com/sciter-sdk/go-sciter/window"
)

func main() {
// make rect for window
rect := sciter.NewRect(100, 100, 300, 500)
// create a window using upper rect
win, _ := window.New(sciter.SW_MAIN|sciter.SW_CONTROLS|sciter.SW_ENABLE_DEBUG, rect)
win.LoadFile("./notepad.html")
win.SetTitle("Notepad+-")

// function closes appliaction
win.DefineFunction("closeWindow", closeApplication)

// save function called from TIScript 
// to save file
win.DefineFunction("save", save) 

// open function callled from TIScript
// to open file
win.DefineFunction("open", open)

win.Show()
win.Run()
}

// closeApplication stops 
// process and application exits
func closeApplication(vals ...*sciter.Value) *sciter.Value {
syscall.Exit(0)
return nil
}

// save takes input from TIScript function
// generates new file and stores data
// recived from sciter call
func save(vals ...*sciter.Value) *sciter.Value {
fmt.Println("Saving Your Document")
path := vals[0]
doc := vals[1]
processedFilePath := strings.Replace(path.String(), "file://", "", 1)
file, fileCreationError := os.OpenFile(processedFilePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.ModePerm)

if fileCreationError != nil {
fmt.Println("failed to create a blank file ", fileCreationError.Error())
return nil
}
defer file.Close()
charCount, writeError := file.WriteString(doc.String())
if writeError != nil {
fmt.Println("failed to write on file due to : ", writeError.Error())
return nil
}
fmt.Println("chars written ", charCount)
fmt.Println("we got path as ", path.String())
return nil
}


// open takes filepath as input from TIScript function
// opens that file reads it and returns to TIScript
func open(vals ...*sciter.Value) *sciter.Value {
fmt.Println("Saving Your Document")
path := vals[0]
processedFilePath := strings.Replace(path.String(), "file://", "", 1)

readBytes, readError := ioutil.ReadFile(processedFilePath)

if readError != nil {
fmt.Println("failed to write on file due to : ", readError.Error())
return nil
}
fmt.Println("chars written ", len(readBytes))
fmt.Println("we got path as ", path.String())
return sciter.NewValue(string(readBytes))
}

Link to Source Code

Comments