Add initial release

This commit is contained in:
No Time To Play 2023-09-09 15:24:02 +00:00
parent 872c0ccd48
commit cf4b055749
5 changed files with 926 additions and 2 deletions

5
NEWS.md Normal file
View File

@ -0,0 +1,5 @@
# Genoa Banner Creator project news
## [0.3 alpha] - 2023-09-09
Initial release

View File

@ -1,3 +1,52 @@
# genoa
# Intro to Genoa Banner Creator
a tool to generate original art
This is yet another experimental tool for procedural art. Use simple scripts to describe graphical banners and posters with many repetitive elements, then export them as SVG for further use. Scripts can be edited and previewed in place for rapid turnaround.
Rationale: I make vector art by hand (see the Pocket Guide to Writing SVG, by Joni Trythall). It's a wonderful artistic medium, but turnaround can be slow between my text editor and image viewer. Also, that way it's hard to add repetitive elements. Genoa is meant to help with that as a first step, and for quick iteration of ideas.
## Availability
As of September 2023, Genoa is at a very early alpha stage: incomplete, undocumented and probably buggy. Use at your own risk. System requirements:
- Tcl/Tk 8.6 with tklib
- screen resolution:
- minimum 800x600
- recommended 1280x800
## Usage
Genoa is powered by a little language based on Tcl. Right now it can generate its own promotional banner, like this:
```tcl
# The viewport declaration can be anywhere.
viewport 75 50 630 500
width 0
fill darkslategray
box 75 50 630 500
width 10
fill ""
proc circles {cx cy} {
repeat 10 {
incr i 30
circle $cx $cy $i
}
}
color darkred; circles 200 200
color green; circles 500 100
color darkblue; circles 400 400
font "Noto Sans"; size 48
color white; set j 0
foreach i {GENERATE ORIGINAL ART} {
incr j 64
text 225 [+ 225 $j] $i
}
```
Simply paste it in then hit Preview and/or Export. No need to save first.
## License and contact
Genoa is open source under the MIT License. Feedback is welcome. I can be found most readily as @notimetoplay@elekk.xyz on Mastodon, or else in the #ctrl-c channel of tilde.chat (on IRC).

25
banner.tcl Normal file
View File

@ -0,0 +1,25 @@
# The viewport declaration can be anywhere.
viewport 75 50 630 500
width 0
fill darkslategray
box 75 50 630 500
width 10
fill ""
proc circles {cx cy} {
repeat 10 {
incr i 30
circle $cx $cy $i
}
}
color darkred; circles 200 200
color green; circles 500 100
color darkblue; circles 400 400
font "Noto Sans"; size 48
color white; set j 0
foreach i {GENERATE ORIGINAL ART} {
incr j 64
text 225 [+ 225 $j] $i
}

835
genoa.tcl Normal file
View File

@ -0,0 +1,835 @@
#!/usr/bin/env tclsh
#
# Genoa Banner Creator: a tool to generate original art.
# Copyright 2023 Felix Pleșoianu <https://felix.plesoianu.ro/>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
package require Tcl 8.6
package require Tk 8.6
package require getstring
namespace import getstring::*
set window_title "Genoa Banner Creator"
set about_text "Tool to Generate Original Art\nv0.3 alpha (8 Sep 2023)"
set credits_text "Made by No Time To Play\nMIT License"
set site_link "https://notimetoplay.org/"
namespace path {::tcl::mathop ::tcl::mathfunc}
set drawing(color) "black"
set drawing(fill) ""
set drawing(font) "Times"
set drawing(size) "10"
set drawing(width) 1
set d_list {}
set meta(width) 200
set meta(height) 200
set meta(viewBox) "0 0 200 200"
set overlay {0 0 200 200}
interp create -safe runner
runner eval {
proc repeat {times code} {
for {set i 0} {$i < $times} {incr i} {
uplevel 1 $code
}
}
namespace path {::tcl::mathop ::tcl::mathfunc}
}
proc line {x1 y1 x2 y2} {
global d_list drawing
lappend d_list [dict create type "line" \
coords [list $x1 $y1 $x2 $y2] \
x1 $x1 y1 $y1 x2 $x2 y2 $y2 \
-outline $drawing(color) -width $drawing(width)]
}
proc box {x y w h} {
global d_list drawing
lappend d_list [dict create type "box" \
coords [list $x $y [+ $x $w] [+ $y $h]] \
x $x y $y width $w height $h \
-outline $drawing(color) -width $drawing(width) \
-fill $drawing(fill)]
}
proc circle {cx cy r} {
global d_list drawing
set coords [list [- $cx $r] [- $cy $r] [+ $cx $r] [+ $cy $r]]
lappend d_list [dict create type "oval" coords $coords \
cx $cx cy $cy rx $r ry $r \
-outline $drawing(color) -width $drawing(width) \
-fill $drawing(fill)]
}
proc oval {cx cy r1 r2} {
global d_list drawing
set coords [list [- $cx $r1] [- $cy $r2] [+ $cx $r1] [+ $cy $r2]]
lappend d_list [dict create type oval coords $coords \
cx $cx cy $cy rx $rx ry $ry \
-outline $drawing(color) -width $drawing(width) \
-fill $drawing(fill)]
}
proc make_text {x y t} {
global d_list drawing
lappend d_list [dict create type "text" coords [list $x $y] \
x $x y $y -text $t -fill $drawing(color) \
-font [list $drawing(font) $drawing(size)] \
font-family $drawing(font) font-size "$drawing(size)pt"]
}
proc color {c} {
global drawing
set drawing(color) $c
}
proc fill {f} {
global drawing
set drawing(fill) $f
}
proc width {w} {
global drawing
set drawing(width) $w
}
proc set_font {f} {
global drawing
set drawing(font) $f
}
proc size {s} {
global drawing
set drawing(size) $s
}
proc viewport {x y w h} {
global meta overlay
set meta(viewBox) "$x $y $w $h"
set meta(width) $w
set meta(height) $h
set overlay [list $x $y [+ $x $w] [+ $y $h]]
}
runner alias line line
runner alias box box
runner alias circle circle
runner alias oval oval
runner alias text make_text
runner alias color color
runner alias fill fill
runner alias width width
runner alias font set_font
runner alias size size
runner alias viewport viewport
set file_types {
{"Genoa scripts" ".tcl"}
{"All files" ".*"}
}
set file_name ""
set search_term ""
set _word_wrap 1
namespace eval font_size {
variable minimum 6
variable default 11
variable maximum 16
namespace export increase decrease reset
namespace ensemble create
variable current $default
proc increase {widget {family "Courier"}} {
variable current
variable maximum
if {$current < $maximum} {
incr current
}
$widget configure -font "$family $current"
}
proc decrease {widget {family "Courier"}} {
variable current
variable minimum
if {$current > $minimum} {
incr current -1
}
$widget configure -font "$family $current"
}
proc reset {widget {family "Courier"}} {
variable current
variable default
set current $default
$widget configure -font "$family $current"
}
}
namespace eval tk_util {
proc pack_scrolled widget {
set parent [winfo parent $widget]
if {$parent == "."} {
set scroll .scroll
} else {
set scroll $parent.scroll
}
ttk::scrollbar $scroll -orient "vertical" \
-command "$widget yview"
pack $widget -side "left" -fill "both" -expand 1
$widget configure -yscrollcommand "$scroll set"
pack $scroll -side "right" -fill y
}
proc load_text {widget content} {
$widget delete 1.0 end
$widget insert end $content
$widget edit reset
$widget edit modified 0
}
proc text_selection widget {
if {[llength [$widget tag ranges sel]] > 0} {
return [$widget get sel.first sel.last]
} else {
return ""
}
}
proc paste_content widget {
if {[llength [$widget tag ranges sel]] > 0} {
$widget delete sel.first sel.last
}
tk_textPaste $widget
}
proc select_all widget {
$widget tag remove sel 1.0 end
$widget tag add sel 1.0 end
}
proc highlight_text {widget content {start "1.0"}} {
set idx [$widget search -nocase $content $start]
if {$idx ne ""} {
set len [string length $content]
set pos "$idx +$len chars"
$widget tag remove "sel" "1.0" "end"
$widget tag add "sel" $idx $pos
$widget mark set "insert" $pos
$widget see "insert"
focus $widget
}
return $idx
}
proc open_line widget {
$widget insert "insert +0 chars" "\n"
$widget mark set "insert" "insert -1 chars"
}
proc word_wrap widget {
if {[$widget cget -wrap] eq "word"} {
$widget configure -wrap "none"
} else {
$widget configure -wrap "word"
}
}
proc full_screen window {
if {[wm attributes $window -fullscreen]} {
wm attributes $window -fullscreen 0
} else {
wm attributes $window -fullscreen 1
}
}
}
wm title . $window_title
option add *tearOff 0
. configure -padx 4
if {[tk windowingsystem] == "x11"} {
ttk::style theme use "clam"
}
pack [ttk::frame .toolbar] -side top -pady 4
ttk::frame .status
ttk::label .status.line -relief sunken -textvar status
ttk::sizegrip .status.grip
pack .status -side bottom -fill x -pady 4
pack .status.line -side left -fill x -expand 1
pack .status.grip -side right -anchor s
ttk::panedwindow .workspace -orient horizontal
ttk::frame .workspace.edit
ttk::frame .workspace.view
.workspace add .workspace.edit -weight 1
.workspace add .workspace.view -weight 1
set editor [text .workspace.edit.text \
-width 40 -height 25 -wrap "word" -undo 1]
font_size::reset $editor
tk_util::pack_scrolled $editor
set preview [canvas .workspace.view.canvas]
pack $preview -side "left" -fill "both" -expand 1
pack .workspace -side top -fill both -expand 1
ttk::button .toolbar.new -text "New" -width 8 -under 0 -command do_new
ttk::button .toolbar.bOpen -text "Open" -width 8 -under 0 -command do_open
ttk::button .toolbar.save -text "Save" -width 8 -under 0 -command do_save
ttk::separator .toolbar.sep1 -orient vertical
ttk::button .toolbar.find -text "Find" -width 8 -under 0 -command do_find
ttk::button .toolbar.again -text "Again" -width 8 -under 1 -command find_again
ttk::separator .toolbar.sep2 -orient vertical
ttk::button .toolbar.render -text "Render" -width 8 -under 0 \
-command {do_render; do_preview}
ttk::button .toolbar.export -text "Export" -width 8 -under 0 -command do_export
ttk::button .toolbar.stats -text "Stats" -width 8 -under 1 -command show_stats
pack .toolbar.new -side left
pack .toolbar.bOpen -side left
pack .toolbar.save -side left
pack .toolbar.sep1 -side left -padx 4 -pady 4 -fill y
pack .toolbar.find -side left
pack .toolbar.again -side left
pack .toolbar.sep2 -side left -padx 4 -pady 4 -fill y
pack .toolbar.render -side left
pack .toolbar.export -side left
pack .toolbar.stats -side left
. configure -menu [menu .menubar]
set m [menu .menubar.mFile]
$m add command -label "New" -command do_new -under 0 -accel "Ctrl-N"
$m add command -label "Open..." -command do_open -under 0 -accel "Ctrl-O"
$m add command -label "Save" -command do_save -under 0 -accel "Ctrl-S"
$m add separator
$m add command -label "Save as..." -command do_save_as -under 5
$m add command -label "Reload" -command do_reload -under 0
$m add command -label "Statistics" -command show_stats -under 1 -accel "Ctrl-T"
$m add separator
$m add command -label "Quit" -command do_quit -under 0 -accel "Ctrl-Q"
.menubar add cascade -menu .menubar.mFile -label "File" -underline 0
set m [menu .menubar.edit]
$m add command -label "Undo" -command {$editor edit undo} \
-underline 0 -accelerator "Ctrl-Z"
$m add command -label "Redo" -command {$editor edit redo} \
-underline 0 -accelerator "Ctrl-Y"
$m add separator
$m add command -label "Cut" -command {tk_textCut $editor} \
-underline 0 -accelerator "Ctrl-X"
$m add command -label "Copy" -command {tk_textCopy $editor} \
-underline 1 -accelerator "Ctrl-C"
$m add command -label "Paste" -command {tk_util::paste_content $editor} \
-underline 0 -accelerator "Ctrl-V"
$m add separator
$m add command -label "Select all" -under 7 -accel "Ctrl-A" \
-command {tk_util::select_all $editor; break}
$m add command -label "Find..." -command do_find -under 0 -accel "Ctrl-F"
$m add command -label "Again" -command find_again -under 1 -accel "Ctrl-G"
.menubar add cascade -menu .menubar.edit -label "Edit" -underline 0
set m [menu .menubar.view]
$m add command -label "Render" -underline 0 -accelerator "F5" \
-command {do_render; do_preview}
$m add command -label "Export..." -command do_export \
-underline 0 -accelerator "Ctrl-E"
$m add separator
$m add checkbutton -label "Word wrap" -under 0 -var _word_wrap \
-command {tk_util::word_wrap $editor}
$m add separator
$m add command -label "Bigger font" -under 0 -accel "Ctrl +" \
-command {font_size incr $editor}
$m add command -label "Smaller font" -under 0 -accel "Ctrl -" \
-command {font_size decr $editor}
$m add command -label "Reset font" -under 0 -accel "Ctrl-0" \
-command {font_size reset $editor}
$m add separator
if {[llength [info commands "console"]] > 0} {
$m add command -label "Console" -command {console show} \
-underline 5 -accelerator "Ctrl-L"
}
$m add checkbutton -label "Full screen" -command {tk_util::full_screen .} \
-underline 10 -accelerator "F11" -var _full_screen
.menubar add cascade -menu .menubar.view -label "View" -underline 0
set m [menu .menubar.help]
$m add command -label "About" -command {alert $about_text} -under 0
$m add command -label "Credits" -command {alert $credits_text} -under 0
$m add command -label "Website" -command {open_in_app $site_link} -under 0
.menubar add cascade -menu .menubar.help -label "Help" -underline 0
wm protocol . WM_DELETE_WINDOW do_quit
bind $editor <<Modified>> show_modified
bind . <Control-n> do_new
bind . <Control-o> do_open
bind . <Control-s> do_save
bind . <Control-t> show_stats
bind . <Control-q> do_quit
bind . <Command-n> do_new
bind . <Command-o> do_open
bind . <Command-s> do_save
bind . <Command-t> show_stats
bind . <Command-q> do_quit
# Undo, cut and copy are already bound to their usual keys by default.
# Many bindings here have to override broken defaults.
bind $editor <Control-y> {$editor edit redo}
bind $editor <Control-v> {tk_util::paste_content $editor; break}
bind $editor <Control-a> {tk_util::select_all $editor; break}
bind . <Control-f> do_find
bind . <Control-g> find_again
bind $editor <Command-y> {$editor edit redo}
bind $editor <Command-v> {tk_util::paste_content $editor; break}
bind $editor <Command-a> {tk_util::select_all $editor; break}
bind . <Command-f> do_find
bind . <Command-g> find_again
bind Text <Control-o> {}
bind . <Control-equal> {font_size::increase $editor}
bind . <Control-minus> {font_size::decrease $editor}
bind . <Control-Key-0> {font_size::reset $editor}
bind . <Command-equal> {font_size::increase $editor}
bind . <Command-minus> {font_size::decrease $editor}
bind . <Command-Key-0> {font_size::reset $editor}
bind . <F5> {do_render; do_preview}
bind . <Control-r> {do_render; do_preview}
bind . <Command-r> {do_render; do_preview}
bind . <F11> {tk_util::full_screen .}
if {[llength [info commands "console"]] > 0} {
bind . <Control-l> {console show}
bind . <Command-l> {console show}
}
proc show_modified {} {
global status editor
if {[$editor edit modified]} {
set status "(modified)"
}
}
proc do_new {} {
global status file_name editor window_title
if {[$editor edit modified]} {
set answer [tk_messageBox -parent . \
-type "yesno" -icon "question" \
-title $window_title \
-message "New file?" \
-detail "File is unsaved.\nStart another?"]
if {!$answer} {
set status "New file canceled."
return
}
}
wm title . $window_title
set file_name ""
$editor delete "1.0" "end"
$editor edit reset
$editor edit modified 0
set status [clock format [clock seconds]]
}
proc do_open {} {
global status file_types file_name editor window_title
if {[$editor edit modified]} {
set answer [tk_messageBox -parent . \
-type "yesno" -icon "question" \
-title $window_title \
-message "Open another file?" \
-detail "File is unsaved.\nOpen another?"]
if {!$answer} {
set status "Opening canceled."
return
}
}
set choice [tk_getOpenFile -parent . \
-title "Open existing file" \
-initialdir [file_dir $file_name] \
-filetypes $file_types]
if {[string length $choice] == 0} {
set status "Opening canceled."
} elseif {![file isfile $choice]} {
tk_messageBox -parent . \
-type "ok" -icon "error" \
-title $window_title \
-message "Error opening file" \
-detail "File not found: $choice"
} elseif {[load_file $choice]} {
set file_name $choice
}
}
proc load_file full_path {
global status editor window_title
set fn [file tail $full_path]
try {
set f [open $full_path]
tk_util::load_text $editor [read $f]
set status "Opened $fn"
wm title . "$fn | $window_title"
return 1
} on error e {
tk_messageBox -parent . \
-type "ok" -icon "error" \
-title $window_title \
-message "Error opening file" \
-detail $e
return 0
} finally {
close $f
}
}
proc file_dir name {
if {$name ne ""} {
return [file dirname $name]
} else {
return [pwd]
}
}
proc do_save {} {
global file_name
if {$file_name eq ""} {
do_save_as
} else {
save_file $file_name
}
}
proc do_save_as {} {
global file_name file_types status
set choice [tk_getSaveFile -parent . \
-title "Save file as..." \
-initialdir [file_dir $file_name] \
-filetypes $file_types]
if {[string length $choice] == 0} {
set status "Save canceled."
} elseif {[save_file $choice]} {
set file_name $choice
}
}
proc save_file full_path {
global status editor window_title
set fn [file tail $full_path]
set data [$editor get "1.0" "end"]
set data [string trimright $data "\n"]
try {
set f [open $full_path "w"]
puts $f $data
flush $f
$editor edit modified 0
set status "Saved $fn"
wm title . "$fn | $window_title"
return 1
} on error e {
tk_messageBox -parent . \
-type "ok" -icon "error" \
-title $window_title \
-message "Error saving file" \
-detail $e
return 0
} finally {
close $f
}
}
proc do_reload {} {
global file_name status window_title
if {$file_name eq ""} {
tk_messageBox -parent . \
-type "ok" -icon "warning" \
-title $window_title \
-message "Can't reload." \
-detail "The file was never saved."
} else {
set answer [tk_messageBox -parent . \
-type "yesno" -icon "question" \
-title $window_title \
-message "Reload file?" \
-detail "Reload last save?"]
if {$answer eq "yes"} {
load_file $file_name
} else {
set status "Reloading canceled."
}
}
}
proc show_stats {} {
global editor d_list window_title
do_render
set data [$editor get "1.0" "end"]
set msg "Project statistics"
set clean [string trimright $data "\n"]
set lines [llength [split $clean "\n"]]
set shape [llength $d_list]
set stats "$lines lines of code\n$shape shapes defined"
tk_messageBox -parent . \
-type "ok" -icon "info" -title $window_title \
-message $msg -detail $stats
}
proc do_quit {} {
global editor window_title
if {[$editor edit modified]} {
set answer [tk_messageBox -parent . \
-type "yesno" -icon "question" \
-title $window_title \
-message "Quit Genoa?" \
-detail "File is unsaved.\nQuit anyway?"]
} else {
set answer "yes"
}
if {$answer eq "yes"} {
destroy .
}
}
proc do_find {} {
global search_term status editor
set term [tk_util::text_selection $editor]
set ret [tk_getString .gs answer "Search pattern:" \
-title "Find" -entryoptions "-textvar search_term"]
if {!$ret} {
set status "Search canceled."
return
}
set search_term $answer; # Possibly redundant
step_search
}
proc find_again {} {
global search_term status
if {$search_term eq ""} {
do_find
} else {
step_search
}
}
proc step_search {} {
global search_term status editor
set res [tk_util::highlight_text $editor $search_term "insert"]
if {$res eq ""} {
set search_term ""
set status "Nothing found."
}
}
proc do_render {} {
global d_list editor window_title
set d_list {}
try {
runner eval [$editor get 1.0 end]
} on error e {
tk_messageBox -parent . \
-type "ok" -icon "error" \
-title $window_title \
-message "Error in script" \
-detail $e
}
}
proc do_preview {} {
global d_list overlay preview
$preview delete all
foreach i $d_list {
shape2canvas $i $preview
}
$preview create rectangle $overlay -outline red -dash -
}
proc shape2canvas {shape canvas} {
switch [dict get $shape type] {
"line" {
$canvas create line [dict get $shape coords] \
-fill [dict get $shape -outline] \
-width [dict get $shape -width]
}
"box" {
$canvas create rectangle \
[dict get $shape coords] \
-outline [dict get $shape -outline] \
-width [dict get $shape -width] \
-fill [dict get $shape -fill]
}
"oval" {
$canvas create oval [dict get $shape coords] \
-outline [dict get $shape -outline] \
-width [dict get $shape -width] \
-fill [dict get $shape -fill]
}
"text" {
$canvas create text [dict get $shape coords] \
-text [dict get $shape -text] \
-font [dict get $shape -font] \
-fill [dict get $shape -fill] \
-anchor sw
}
default { error "Unknown shape." }
}
}
proc do_export {} {
global file_name
set file_types {
{"SVG drawings" ".svg"}
{"All files" ".*"}
}
set choice [tk_getSaveFile -parent . \
-title "Export file as..." \
-initialdir [file_dir $file_name] \
-filetypes $file_types]
if {$choice == ""} {
set status "Export canceled."
} else {
export_file $choice
}
}
proc export_file full_path {
global status d_list meta window_title
set fn [file tail $full_path]
try {
do_render
set f [open $full_path "w"]
puts $f "<svg xmlns='http://www.w3.org/2000/svg' width='$meta(width)' height='$meta(height)' viewBox='$meta(viewBox)'>"
foreach i $d_list {
puts $f [shape2svg $i]
}
puts $f "</svg>"
flush $f
set status "Exported $fn"
return 1
} on error e {
tk_messageBox -parent . \
-type "ok" -icon "error" \
-title $window_title \
-message "Export error" \
-detail $e
return 0
} finally {
close $f
}
}
proc shape2svg shape {
switch [dict get $shape type] {
"line" {
set stroke [dict get $shape -outline]
if {$stroke == ""} { set stroke "transparent" }
return "<line x1='[dict get $shape x1]' y1='[dict get $shape y1]' x2='[dict get $shape x2]' y2='[dict get $shape y2]' stroke='$stroke' stroke-width='[dict get $shape -width]'/>"
}
"box" {
set fill [dict get $shape -fill]
if {$fill == ""} { set fill "transparent" }
set stroke [dict get $shape -outline]
if {$stroke == ""} { set stroke "transparent" }
return "<rect x='[dict get $shape x]' y='[dict get $shape y]' width='[dict get $shape width]' height='[dict get $shape height]' fill='$fill' stroke='[dict get $shape -outline]' stroke-width='[dict get $shape -width]'/>"
}
"oval" {
set fill [dict get $shape -fill]
if {$fill == ""} { set fill "transparent" }
set stroke [dict get $shape -outline]
if {$stroke == ""} { set stroke "transparent" }
return "<ellipse cx='[dict get $shape cx]' cy='[dict get $shape cy]' rx='[dict get $shape rx]' ry='[dict get $shape ry]' fill='$fill' stroke='$stroke' stroke-width='[dict get $shape -width]'/>"
}
"text" {
set fill [dict get $shape -fill]
if {$fill == ""} { set fill "transparent" }
return "<text x='[dict get $shape x]' y='[dict get $shape y]' fill='$fill' font-family='[dict get $shape font-family]' font-size='[dict get $shape font-size]'>[dict get $shape -text]</text>"
}
default { error "Unknown shape." }
}
}
proc alert message {
global window_title
tk_messageBox -parent . \
-type "ok" -icon "info" \
-title $window_title \
-message $message
}
proc open_in_app link {
global status
if {[auto_execok "xdg-open"] ne ""} {
catch {exec "xdg-open" $link &}
} elseif {[auto_execok "open"] ne ""} {
catch {exec "open" $link &}
} elseif {[auto_execok "start"] ne ""} {
catch {exec "start" $link &}
} else {
set status "Can't open website."
}
}
set status [clock format [clock seconds]]
if {[llength $argv] > 0} {
set fn [lindex $argv 0]
if {![file exists $fn]} {
set file_name [file normalize $fn]
set fn [file tail $file_name]
wm title . "$fn | Genoa"
} elseif {[load_file $fn]} {
set file_name [file normalize $fn]
}
}

10
icon2.tcl Normal file
View File

@ -0,0 +1,10 @@
viewport 0 0 32 32
color "";
fill red; circle 8 12 12
fill green; circle 24 16 12
fill blue; circle 16 24 12
font "Noto Sans"; size 24
color white; text 4 28 G