Skip to main content

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

Popular posts from this blog

Google blogger Ideas panel

Google blogger Ideas  I opened by blogger today, and..   I got this.  Google blogger Ideas  A panel suggesting a topic on which I can write my next blog. It's fetching unanswered question from web according to your previous post and topics. It was something, I was really looking for, after all it takes time to finding subject on which to write next and still being in the same niche.  Awesome feature Blogger! 

Apache : setup basic auth with apache in windows

Authentication is any process by which you verify that someone is who they claim they are. Authorization is any process by which someone is allowed to be where they want to go or to have information that they want to have. I will show here how to set up basic auth on the apache with windows. Pre-requests  Windows VPS Apache server ( That's it ) ( In windows it might be difficult to setup the Apache alone. So instead use something ling xampp , wamp or laragon .) RestClient (  I personally use the postman , but you can use your preferable client)  Windows VPS provider Steps  Enable the necessary modules in the Apache Create the password file Set the auth directives in the virtual host file. Verify basic auth. Enable the  necessary   modules  in the Apache Open the httpd.conf file in the apache's conf folder. httpd.conf file Enable the necessary modules to make the basic auth working. Necessary modules  mod_auth_basic

Firebase - update a spacific fields of single element of object of array in firestore

Firebase - update a spacific fields of single element of object of array in firestore  Its actully advisable to use map instead of array when ever it is possible. But, there are cetain cases where you don't have option to do so.  For example, you are directly saving the response from some outer source without any modification and they send you an array. In this case you will have array to work with. Firestore does not support array here is why  "bad things can happen if you have multiple clients all trying to update or delete array elements at specific indexes. In the past, Cloud Firestore addressed these issues by limiting what you can do with arrays " For more details information you can refer to Kato Richardson post Best Practices: Arrays in Firebase .  Firestore document having array [ used from stackoverflow question ] Suppose you have array of object something like shown in array. Now you want to update endTime field of the object on the index [1]