WJG (03/03/18) At home my family prefer Word to LibreOffice, never mind. The main reason they prefer it is nothing at all to with functionality. They don't remember where embedded menu options are so prefer the switchable toolbars that Word has. So, I thought that I'd put something together like this as a simple text editor in
Gnocl. At first I wasn't convinced of the speed benefits but now I am. Less clutter on screen, more space for the edit widget and no waiting for menus to build themselves. Added to this, the programming was so much less complicated. I saw this mixture of tabbed menus and pull-down menus first in the Maya CGI animation system back in the 1990s, an application with a lot of widgets. So, perhaps this UI paradigm is worth a second glance.
The application uses
Gnocl rather than Tk to create the UI. The script takes advantage of the widget -name option to specify objects rather than using their widget-ids. Makes life a lot easier! Bye-bye global declarations and namespaces.
Finally, no need to create custom tags; the
Gnocl text widget does it for you with the -useMarkup option to create a key set of Pango/HTML markup tags.
## test-menu-tabs.tcl
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"
if { [catch { package present Gnocl } ] } { package require Gnocl }
#---------------
# Application tooltips
#---------------
#
array set tooltips {
FILE,NEW "Create new file"
FILE,OPEN "Open an existing project file"
FILE,OPEN,ARROW "Select a recent file"
FILE,SAVE "Save current document"
FILE,SAVEAS "Save document with a different name"
FILE,QUIT "End the application"
EDIT,UNDO "Undo last edit"
EDIT,REDO "Redo last change"
EDIT,CUT "Cut selected text, copy to clipboard"
EDIT,COPY "Copy selected text to the clipboard (no markup)"
EDIT,PASTE "Paste clipboard text in the document"
EDIT,DELETE "Delete selected text"
EDIT,SELECTALL "Select all text in the document"
EDIT,PREFERENCES "Set application preferences"
HELP,ABOUT "Show details about this application"
HELP,HELP "Load application help file"
}
#---------------
# Create tab menus
#---------------
# Arguments:
# val
# args list of options passed to the proc
# Returns:
# none
#
proc tabMenus { val args } {
# set defaults and assign options values from args
array set opt [list iconSize 1 style icons]
array set opt [string map {- {}} $args]
set res ""
set notebook [gnocl::notebook -name TABMENU -homogeneous 1 -borderWidth 0]
$notebook addPage [gnocl::toolBar -name FILE -style $opt(style) -iconSize $opt(iconSize) ] %__File
$notebook addPage [gnocl::richTextToolBar -name STYLES -iconSize $opt(iconSize)] %__Styles
$notebook addPage [gnocl::toolBar -name EDIT -style $opt(style) -iconSize $opt(iconSize)] %__Edit
$notebook addPage [gnocl::toolBar -name HELP -style $opt(style) -iconSize $opt(iconSize)] %__Help
# do something!
set rc_menu [gnocl::menuRecentChooser \
-showNumbers 1 \
-limit 5 \
-showIcons 1 \
-showNotFound 0 \
-showPrivate 1 \
-showTips 1 \
-patterns "*.txt *.TXT" \
-onClicked {
if { ![file exists %f ] } {
gnocl::dialog -title "FILE ERROR!" \
-type warning \
-text "%f\ndoes not exist\n" \
-setSize 0.125 \
-modal 1 \
-onResponse { return }
$app(status) push "Recent file load cancelled: file %f does not exist."
return
}
set app(fname) %f
openRecent } ]
FILE add item -icon %#New -name FILE,NEW -tooltip $::tooltips(FILE,NEW)
FILE add menuButton \
-name FILE,OPEN \
-icon %#Open \
-text Open \
-menu $rc_menu \
-tooltip $::tooltips(FILE,OPEN) \
-arrowTooltip $::tooltips(FILE,OPEN,ARROW) \
-onClicked { openFile }
#FILE add item -icon %#Open -name FILE,OPEN -tooltip $::tooltips(FILE,OPEN)
FILE add item -icon %#Save -name FILE,SAVE -tooltip $::tooltips(FILE,OPEN)
FILE add item -icon %#SaveAs -name FILE,SAVEAS -tooltip $::tooltips(FILE,SAVEAS)
FILE add separator
FILE add item -icon %#Quit -name FILE,QUIT -tooltip $::tooltips(FILE,QUIT)
EDIT add item -icon %#Undo -name EDIT,UNDO -tooltip $::tooltips(EDIT,UNDO)
EDIT add item -icon %#Redo -name EDIT,REDO -tooltip $::tooltips(EDIT,REDO)
EDIT add separator
EDIT add item -icon %#Cut -name EDIT,CUT -tooltip $::tooltips(EDIT,CUT)
EDIT add item -icon %#Copy -name EDIT,COPY -tooltip $::tooltips(EDIT,COPY)
EDIT add item -icon %#Paste -name EDIT,PASTE -tooltip $::tooltips(EDIT,PASTE)
EDIT add item -icon %#Delete -name EDIT,DELETE -tooltip $::tooltips(EDIT,DELETE)
EDIT add separator
EDIT add item -icon %#SelectAll -name EDIT,SELECTALL -tooltip $::tooltips(EDIT,SELECTALL)
EDIT add separator
EDIT add item -icon %#Preferences -name EDIT,PREFERENCES -tooltip $::tooltips(EDIT,PREFERENCES)
HELP add item -icon %#Help -name HELP,HELP -tooltip $::tooltips(HELP,HELP)
HELP add item -icon %#About -name HELP,ABOUT -tooltip $::tooltips(HELP,ABOUT)
return $notebook
}
#---------------
# Description
#---------------
# Arguments:
# none
# Returns:
# none
#
proc openFile {} {
global app
set myFilters {Text {*.txt *.TXT} }
set fname [gnocl::fileChooserDialog -title "Select File..." -fileFilters $myFilters -currentFolder $::env(HOME)]
if { $fname == "" } {
STATUS push "Operation \"Open file...\" cancelled."
return }
set fp [open $fname "r"]
TEXT clear
TEXT insert end [read $fp]
close $fp
MAIN configure -title [file tail $fname]
set app(fname) $fname
STATUS push "File Open: $app(fname)"
}
#---------------
# Description
#---------------
# Arguments:
# none
# Returns:
# none
#
proc openRecent {} {
global app
set fp [open $app(fname) "r"]
TEXT clear
TEXT insert end [read $fp]
close $fp
MAIN configure -title [file tail $app(fname)]
STATUS push "Open File Recent: [file tail $app(fname)] completed."
}
#---------------
# Description
#---------------
# Arguments:
# none
# Returns:
# none
#
proc saveFile {} {
global app
set fp [open $app(fname) "w"]
puts $fp [TEXT get ]
close $fp
STATUS push "Operation \"Save...\" completed."
}
#---------------
# Description
#---------------
# Arguments:
# val
# args
# Returns:
# res
#
proc saveAsFile {} {
global app
set myFilters {Text {*.txt *.TXT} }
set fname [gnocl::fileChooserDialog -action save -title "Save File As..." -fileFilters $myFilters -currentFolder $::env(HOME)]
if { $fname == "" } {
STATUS push "Operation \"Save As...\" cancelled."
return }
set fp [open $fname "w"]
puts $fp [TEXT get ]
close $fp
set app(fname) $fname
MAIN configure -title [file tail $app(fname) ]
}
#---------------
# Display details about the application
#---------------
# Arguments:
# msg String to display as the dialog title
# Returns:
# none
#
proc about { {msg ""} } {
gnocl::aboutDialog \
-title $msg \
-programName "Simple Editor" \
-artists {artist1 artist2} \
-authors {author1 author2} \
-comments "This is a comment." \
-copyright "Copyright." \
-documenters {documenter1 documenter2} \
-license "This is a license" \
-version "V1.2.3" \
-website "http://www.gnocl.org" \
-websiteLabel "www.gnocl.org" \
-logo %/gnocl_logo.png
}
#---------------
# Display not yet implemented message
#---------------
# Arguments:
# msg String to display as the dialog title
# Returns:
# none
#
proc nyi { msg } {
gnocl::dialog -title $msg -type info -text "Not yet implemented"
}
#---------------
# Exit the application
#---------------
# Arguments:
# none
# Returns:
# none
#
proc quit {} {
exit
}
#---------------
# Script Main Function
#---------------
# Arguments:
# args not used
# Returns:
# none
#
proc main { args } {
global app
set app(fname) $::env(HOME)/untitled.txt
gnocl::statusBar -name STATUS
gnocl::vBox -name CONTAINER -padding 0
gnocl::text -name TEXT -useMarkup 1 -margins {4 4 0 4} -wrapMode word
gnocl::window -name MAIN -child [CONTAINER] -setSize 0.5 -title [file tail $app(fname)]
CONTAINER add [tabMenus a] -fill 1 -expand 0
CONTAINER add [TEXT] -fill 1 -expand 1
CONTAINER add [STATUS] -fill 1 -expand 0
STYLES configure -text [TEXT]
FILE,OPEN configure -onClicked { openFile }
FILE,SAVE configure -onClicked { saveFile }
FILE,SAVEAS configure -onClicked { saveAsFile }
FILE,NEW configure -onClicked { STATUS push NEWFILE ; TEXT clear}
FILE,QUIT configure -onClicked { quit }
EDIT,UNDO configure -onClicked { TEXT undo }
EDIT,REDO configure -onClicked { TEXT undo }
EDIT,CUT configure -onClicked { TEXT cut }
EDIT,COPY configure -onClicked { TEXT copy }
EDIT,PASTE configure -onClicked { TEXT paste }
EDIT,DELETE configure -onClicked { TEXT erase selectionStart selectionEnd }
EDIT,SELECTALL configure -onClicked { TEXT select start end }
EDIT,PREFERENCES configure -onClicked { nyi "Set Preferences" }
HELP,HELP configure -onClicked { nyi "Application Help" }
HELP,ABOUT configure -onClicked { about "Simple Editor" }
# add keyboard accelerators
set accelerators {
<Ctrl>n { TEXT clear }
<Ctrl>s saveFile
<Ctrl>o openFile
<Ctrl>q quit
<Ctrl>a { TEXT select start end }
<Ctrl>x { TEXT cut }
<Ctrl>c { TEXT copy }
<Ctrl>v { TEXT paste }
<Ctrl>z { TEXT undo }
<Ctrl>y { TEXT redo }
<Ctrl><Alt>p {nyi "Set Preferences"}
<Ctrl>b { TEXT tag apply selectionStart selectionEnd -tags <b> }
<Ctrl>i { TEXT tag apply selectionStart selectionEnd -tags <i> }
<Ctrl>u { TEXT tag apply selectionStart selectionEnd -tags <u> }
F1 {nyi "Help"}
}
foreach {a b} $accelerators {
gnocl::acceleratorGroup -window [MAIN] \
-key $a -onPressed $b
}
STATUS push "Ready!"
}
main