23 changed files with 40596 additions and 105 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,3 @@ |
|||||||
|
1.7.3 |
||||||
|
#First line must be a semantic version number |
||||||
|
#all other lines are ignored. |
||||||
@ -0,0 +1,446 @@ |
|||||||
|
# -*- tcl -*- |
||||||
|
# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from <pkg>-buildversion.txt |
||||||
|
# module template: shellspy/src/decktemplates/vendor/punk/modules/template_module-0.0.4.tm |
||||||
|
# |
||||||
|
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. |
||||||
|
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
# (C) 2025 |
||||||
|
# |
||||||
|
# @@ Meta Begin |
||||||
|
# Application punk::layout 999999.0a1.0 |
||||||
|
# Meta platform tcl |
||||||
|
# Meta license MIT |
||||||
|
# @@ Meta End |
||||||
|
|
||||||
|
|
||||||
|
#EXPERIMENTAL |
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
## Requirements |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
|
||||||
|
|
||||||
|
package require Tcl 8.6- |
||||||
|
package require punk::args |
||||||
|
|
||||||
|
#experimental layout library based on Nic Barker's Clay |
||||||
|
# CLAY(CLAY_ID("parent"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) { |
||||||
|
# // Child element 1 |
||||||
|
# CLAY_TEXT(CLAY_STRING("Hello World"), CLAY_TEXT_CONFIG({ .fontSize = 16 })); |
||||||
|
# // Child element 2 with red background |
||||||
|
# CLAY(CLAY_ID("child"), { .backgroundColor = COLOR_RED }) { |
||||||
|
# // etc |
||||||
|
# } |
||||||
|
# } |
||||||
|
|
||||||
|
#Tcl translation? |
||||||
|
#CLAY ?-opt <val>..? ?childblock? |
||||||
|
#eg |
||||||
|
#CLAY -id <string> -layout <dict> -rectangle <dict> { childblock } |
||||||
|
#where childblock is curly braced code block |
||||||
|
|
||||||
|
# clay -id "parent" -layout [dict create .padding [padding_all 8]] { |
||||||
|
# clay_text "Hello World" [clay_textconfig {.fontsize 16}] |
||||||
|
# clay -id "child" -config {.backgroundColor red} { |
||||||
|
# # etc |
||||||
|
# } |
||||||
|
# } |
||||||
|
|
||||||
|
#for padding on a terminal - we are limited by cell_size - which is not square. (and may vary on terminals??) |
||||||
|
# e.g punk::console::cell_size returns cell wxh |
||||||
|
# -> 10x20 |
||||||
|
# (either queried from terminal - or defaults to 10x20 if can't be queried) |
||||||
|
#so the minimum 'even' padding we can have is 2 space for horizontal, 1 line vertical |
||||||
|
#we can pad a minimum of 1 space horizontall (10 units) and a minimum of 1 line vertically (20 units) |
||||||
|
# CLAY_PADDING {<l> <r> <t> <b>} |
||||||
|
#how then should we handle .padding {16 16 8 8} - quantize by roundup to h 10 and v 20? |
||||||
|
# -> {20 20 20 20} ? |
||||||
|
# or round to nearest vertical and horizontal quanta? |
||||||
|
# -> {20 20 0 0} |
||||||
|
#either way - {16 16 16 16} goes to {20 20 20 20} - which is *close* to equal v h padding |
||||||
|
# - but not perfect visually because fonts within a cell have baseline, topline etc internal padding too? |
||||||
|
|
||||||
|
#building a ui ? |
||||||
|
#clay_begin_layout |
||||||
|
# // build UI here |
||||||
|
#set rendercommands [clay_end_layout] |
||||||
|
#some_render_command $rendercommands |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#eg2 (from introducing clay video https://www.youtube.com/watch?v=DYWTw19_8r4&t=2s) |
||||||
|
#something like the following? |
||||||
|
# (stopped at around the scroll point - need to work out console mouse features. punk::console::mouse_enable ...) |
||||||
|
|
||||||
|
#extracted out common layout 'sizing' for grow in both directions |
||||||
|
#set layoutExpand { |
||||||
|
# width CLAY_SIZING_GROW |
||||||
|
# heigth CLAY_SIZING GROW |
||||||
|
# } |
||||||
|
#extracted out common background panel |
||||||
|
#set contentBackgroundConfig { |
||||||
|
# colour Term-grey |
||||||
|
# frametype arc |
||||||
|
# } |
||||||
|
#proc RenderHeaderButton {} {} ;#?? |
||||||
|
|
||||||
|
#set documents [dict create\ |
||||||
|
# Squirrels "The secret life of Squirrels"\ |
||||||
|
# "Lorem Ipsum" "Orem ipsum dolor sit ..\netc blah"\ |
||||||
|
# "Vacuum instructions" "Chapter 3: Getting Started"\ |
||||||
|
# "Article 4" "Article 4"\ |
||||||
|
# "Article 5" "Article 5"\ |
||||||
|
# ] |
||||||
|
|
||||||
|
#set selectedDocumentIndex 0 |
||||||
|
|
||||||
|
|
||||||
|
#clay_begin_layout |
||||||
|
#clay -id "OuterContainer"\ |
||||||
|
# -item [clay_rectangle {colour web-red}] |
||||||
|
# -layout [clay_layout [dict create\ |
||||||
|
# layoutDirection CLAY_TOP_TO_BOTTOM\ |
||||||
|
# sizing $layoutExpand\ |
||||||
|
# padding {16 16}\ |
||||||
|
# childGap 16\ |
||||||
|
# ]]\ |
||||||
|
# { |
||||||
|
# # Child elements |
||||||
|
# clay -id "HeaderBar"\ |
||||||
|
# -item [clay_rectangle $contentBackgroundConfig]\ |
||||||
|
# -layout [clay_layout { |
||||||
|
# sizing { |
||||||
|
# height 60 |
||||||
|
# width CLAY_SIZING_GROW |
||||||
|
# } |
||||||
|
# padding {8 8} |
||||||
|
# childGap 8 |
||||||
|
# childAlignment { |
||||||
|
# y CLAY_ALIGN_Y_CENTER |
||||||
|
# } |
||||||
|
# }]\ |
||||||
|
# { |
||||||
|
# # Header buttons go here |
||||||
|
# RenderheaderButton [clay_string "File"] |
||||||
|
# RenderheaderButton [clay_string "Edit"] |
||||||
|
# #push last 3 buttons to rhs by using a layout in between to take up the middle space |
||||||
|
# CLAY -layout {sizing {width CLAY_SIZING_GROW}} {} |
||||||
|
# RenderheaderButton [clay_string "Upload"] |
||||||
|
# RenderheaderButton [clay_string "Media"] |
||||||
|
# RenderheaderButton [clay_string "Support"] |
||||||
|
# } |
||||||
|
# clay -id "LowerContent"\ |
||||||
|
# -layout [dict create sizing $layoutExpand childGap 16]\ |
||||||
|
# { |
||||||
|
# clay -id "Sidebar"\ |
||||||
|
# -item [clay_rectangle $contentBackgroundConfig]\ |
||||||
|
# -layout { |
||||||
|
# layoutDirection CLAY_TOP_TO_BOTTOM |
||||||
|
# padding {8 8} |
||||||
|
# childgap {20} |
||||||
|
# sizing { |
||||||
|
# width = 60 |
||||||
|
# height = CLAY_SIZING_GROW |
||||||
|
# }\ |
||||||
|
# }\ |
||||||
|
# { |
||||||
|
# #render dynamic data |
||||||
|
# dict for {title data} $documents { |
||||||
|
# clay -layout {padding {8 8}\ |
||||||
|
# { |
||||||
|
# #fontId, fontSize not really practical on terminal |
||||||
|
# clay_text $title [clay_text_config { |
||||||
|
# textColor cyan |
||||||
|
# }] |
||||||
|
# } |
||||||
|
# } |
||||||
|
# } |
||||||
|
# clay -id "MainContent"\ |
||||||
|
# -item [clay_rectangle $contentBackgroundConfig]\ |
||||||
|
# -scroll {vertical true}\ |
||||||
|
# -layout [dict create\ |
||||||
|
# layoutDirection = CLAY_TOP_TO_BOTTOM\ |
||||||
|
# childGap 20\ |
||||||
|
# padding {16 16 20 20}\ |
||||||
|
# sizing $layoutExpand\ |
||||||
|
# ]\ |
||||||
|
# { |
||||||
|
# set selectedTitle [lindex [dict keys $documents] $selectedDocumentIndex] |
||||||
|
# clay_text $selectedTitle [clay_text_config { |
||||||
|
# textcolour {web-white bold} |
||||||
|
# }] |
||||||
|
# clay_text [dict get $documents $selectedTitle] [clay_text_config { |
||||||
|
# textcolour {web-white} |
||||||
|
# }] |
||||||
|
# } |
||||||
|
# } |
||||||
|
# } |
||||||
|
#set rendercommands [clay_end_layout] |
||||||
|
|
||||||
|
#CLAY_SIZING_GROW |
||||||
|
#CLAY_SIZING_FIT ;#default |
||||||
|
|
||||||
|
tcl::namespace::eval punk::layout { |
||||||
|
variable PUNKARGS |
||||||
|
namespace eval argdoc { |
||||||
|
variable PUNKARGS |
||||||
|
namespace eval argdoc { |
||||||
|
#non-colour SGR codes |
||||||
|
set I "\x1b\[3m" ;# [a+ italic] |
||||||
|
set NI "\x1b\[23m" ;# [a+ noitalic] |
||||||
|
set B "\x1b\[1m" ;# [a+ bold] |
||||||
|
set N "\x1b\[22m" ;# [a+ normal] |
||||||
|
set T "\x1b\[1\;4m" ;# [a+ bold underline] |
||||||
|
set NT "\x1b\[22\;24m\x1b\[4:0m" ;# [a+ normal nounderline] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#elementDeclaration |
||||||
|
|
||||||
|
#data structure (CLAY UIElement struct equivalent) |
||||||
|
#set eg_element [dict create\ |
||||||
|
# position {1 1}\ |
||||||
|
# size {80 24}\ |
||||||
|
# children [list]\ |
||||||
|
#] |
||||||
|
|
||||||
|
|
||||||
|
#elementConfig |
||||||
|
#set eg_item [dict create\ |
||||||
|
# type rectangle\ |
||||||
|
# color {red bold}\ |
||||||
|
# borderColor {yellow} |
||||||
|
# frametype {}\ |
||||||
|
#] |
||||||
|
# etc |
||||||
|
proc DrawRectangle {element} { |
||||||
|
set position [dict get $element position] ;#x y |
||||||
|
set size [dict get $element size] ;#width height |
||||||
|
lassign $size w h |
||||||
|
set bg [dict get $element color] |
||||||
|
set borderColor [dict get $element borderColor] |
||||||
|
set frametype [dict get $element frametype] |
||||||
|
if {$frametype eq ""} { |
||||||
|
#much faster than textblock::frame |
||||||
|
set content [textblock::block $w $h "[a+ $bg] [a]"] |
||||||
|
} else { |
||||||
|
#slow - and frame background colours will always overlap the borders so we can never get a nicely filled arc frame for example |
||||||
|
#The best bordered |
||||||
|
set content [textblock::frame -type $frametype -ansibase [a+ $bg] -ansiborder [a+ $borderColor] -width $w -height $h " "] |
||||||
|
} |
||||||
|
} |
||||||
|
proc RenderElement {element} { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#TODO |
||||||
|
#The clay_begin_layout - clay_end_layout structure suggests the main API may be better represented as an oo object maintaining state |
||||||
|
#we may need to have multiple instances running concurrently |
||||||
|
#Clay conceptually runs in 'immediate' mode - ie recalculating each full layout each time we need to render |
||||||
|
|
||||||
|
variable rendercommands |
||||||
|
proc clay_begin_layout {} { |
||||||
|
variable rendercommands |
||||||
|
set rendercommands [dict create] |
||||||
|
#...? |
||||||
|
} |
||||||
|
proc clay_end_layout {} { |
||||||
|
variable rendercommands |
||||||
|
|
||||||
|
#...? |
||||||
|
|
||||||
|
return $rendercommands |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
variable PUNKARGS |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punk::layout::clay |
||||||
|
@cmd -name punk::layout::clay |
||||||
|
@opts |
||||||
|
-id -type string |
||||||
|
-item -type dict |
||||||
|
-layout -type dict |
||||||
|
-backgroundColor -type list -help\ |
||||||
|
"ANSI colour codes as documented in punk::ansi::a?" |
||||||
|
@values -min 0 -max 1 |
||||||
|
childscript -type script |
||||||
|
}] |
||||||
|
} |
||||||
|
#For initial proof of concept - we will use punk::args for *parsing* as well as documenting |
||||||
|
#For final version - the extra overhead of punk::args::parse may not be suitable for large numbers of |
||||||
|
#calls in 'immediate' mode - so punk::args::parse should be used on 'unhappy' paths only, |
||||||
|
#with a fast switch statement used for actual option parsing. |
||||||
|
#As performance is likely to be an issue - we should also avoid allowing 'prefixes' for arguments |
||||||
|
proc clay {args} { |
||||||
|
set argd [punk::args::parse $args withid ::punk::layout::clay] ;#temporary during concept development - todo: change to manual parsing for performance. |
||||||
|
|
||||||
|
} |
||||||
|
# |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
# Secondary API namespace |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
tcl::namespace::eval punk::layout::lib { |
||||||
|
tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase |
||||||
|
tcl::namespace::path [tcl::namespace::parent] |
||||||
|
|
||||||
|
|
||||||
|
#STEPS |
||||||
|
#Fit Sizing -> Grow Sizing -> Positions -> Draw |
||||||
|
#tree traversal |
||||||
|
#(reverse breadth first) (breadth first) |
||||||
|
# via OpenElement/CloseElement? (expand our grow containers |
||||||
|
# into any available space) |
||||||
|
|
||||||
|
proc OpenElement {element} { |
||||||
|
#pseudo... |
||||||
|
#set layoutDirection $layoutconfig.layoutDirection |
||||||
|
#if {$layoutDirection eq "CLAY_LEFT_TO_RIGHT"} { |
||||||
|
#} else { |
||||||
|
# #CLAY_TOP_TO_BOTTOM |
||||||
|
#} |
||||||
|
} |
||||||
|
proc CloseElement {element} { |
||||||
|
#element.parent.width += element.width |
||||||
|
#element.parent.height += element.height |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
proc SizeContainersAlongAxis {is_x_axis totalSizeToDistribute} { |
||||||
|
} |
||||||
|
proc CalculateFinalLayout {} { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#tcl::namespace::eval punk::layout::system { |
||||||
|
#} |
||||||
|
|
||||||
|
|
||||||
|
# == === === === === === === === === === === === === === === |
||||||
|
# Sample 'about' function with punk::args documentation |
||||||
|
# == === === === === === === === === === === === === === === |
||||||
|
tcl::namespace::eval punk::layout { |
||||||
|
tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase |
||||||
|
variable PUNKARGS |
||||||
|
variable PUNKARGS_aliases |
||||||
|
|
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id "(package)punk::layout" |
||||||
|
@package -name "punk::layout" -help\ |
||||||
|
"EXPERIMENTAL - not done" |
||||||
|
}] |
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
#namespace for custom argument documentation |
||||||
|
proc package_name {} { |
||||||
|
return punk::layout |
||||||
|
} |
||||||
|
proc about_topics {} { |
||||||
|
#info commands results are returned in an arbitrary order (like array keys) |
||||||
|
set topic_funs [info commands [namespace current]::get_topic_*] |
||||||
|
set about_topics [list] |
||||||
|
foreach f $topic_funs { |
||||||
|
set tail [namespace tail $f] |
||||||
|
lappend about_topics [string range $tail [string length get_topic_] end] |
||||||
|
} |
||||||
|
#Adjust this function or 'default_topics' if a different order is required |
||||||
|
return [lsort $about_topics] |
||||||
|
} |
||||||
|
proc default_topics {} {return [list Description *]} |
||||||
|
|
||||||
|
# ------------------------------------------------------------- |
||||||
|
# get_topic_ functions add more to auto-include in about topics |
||||||
|
# ------------------------------------------------------------- |
||||||
|
proc get_topic_Description {} { |
||||||
|
punk::args::lib::tstr [string trim { |
||||||
|
package punk::layout |
||||||
|
An experiment in using CLAY UI style layout on the terminal |
||||||
|
INCOMPLETE... |
||||||
|
status: notes and started framework only - nothing usable. |
||||||
|
} \n] |
||||||
|
} |
||||||
|
proc get_topic_License {} { |
||||||
|
return "MIT" |
||||||
|
} |
||||||
|
proc get_topic_Version {} { |
||||||
|
return "$::punk::layout::version" |
||||||
|
} |
||||||
|
proc get_topic_Contributors {} { |
||||||
|
set authors {{Julian Noble <julian@precisium.com.au}} |
||||||
|
set contributors "" |
||||||
|
foreach a $authors { |
||||||
|
append contributors $a \n |
||||||
|
} |
||||||
|
if {[string index $contributors end] eq "\n"} { |
||||||
|
set contributors [string range $contributors 0 end-1] |
||||||
|
} |
||||||
|
return $contributors |
||||||
|
} |
||||||
|
proc get_topic_concepts {} { |
||||||
|
punk::args::lib::tstr -return string { |
||||||
|
see Nic Barker's youtube video: How Clay's UI Layout Algorithm Works |
||||||
|
https://www.youtube.com/watch?v=by9lQvpvMIc |
||||||
|
} |
||||||
|
} |
||||||
|
# ------------------------------------------------------------- |
||||||
|
} |
||||||
|
|
||||||
|
# we re-use the argument definition from punk::args::standard_about and override some items |
||||||
|
set overrides [dict create] |
||||||
|
dict set overrides @id -id "::punk::layout::about" |
||||||
|
dict set overrides @cmd -name "punk::layout::about" |
||||||
|
dict set overrides @cmd -help [string trim [punk::args::lib::tstr { |
||||||
|
About punk::layout |
||||||
|
}] \n] |
||||||
|
dict set overrides topic -choices [list {*}[punk::layout::argdoc::about_topics] *] |
||||||
|
dict set overrides topic -choicerestricted 1 |
||||||
|
dict set overrides topic -default [punk::layout::argdoc::default_topics] ;#if -default is present 'topic' will always appear in parsed 'values' dict |
||||||
|
set newdef [punk::args::resolved_def -antiglobs -package_about_namespace -override $overrides ::punk::args::package::standard_about *] |
||||||
|
lappend PUNKARGS [list $newdef] |
||||||
|
proc about {args} { |
||||||
|
package require punk::args |
||||||
|
#standard_about accepts additional choices for topic - but we need to normalize any abbreviations to full topic name before passing on |
||||||
|
set argd [punk::args::parse $args withid ::punk::layout::about] |
||||||
|
lassign [dict values $argd] _leaders opts values _received |
||||||
|
punk::args::package::standard_about -package_about_namespace ::punk::layout::argdoc {*}$opts {*}[dict get $values topic] |
||||||
|
} |
||||||
|
} |
||||||
|
# end of sample 'about' function |
||||||
|
# == === === === === === === === === === === === === === === |
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------- |
||||||
|
# register namespace(s) to have PUNKARGS,PUNKARGS_aliases variables checked |
||||||
|
# ----------------------------------------------------------------------------- |
||||||
|
# variable PUNKARGS |
||||||
|
# variable PUNKARGS_aliases |
||||||
|
namespace eval ::punk::args::register { |
||||||
|
#use fully qualified so 8.6 doesn't find existing var in global namespace |
||||||
|
lappend ::punk::args::register::NAMESPACES ::punk::layout |
||||||
|
} |
||||||
|
# ----------------------------------------------------------------------------- |
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
## Ready |
||||||
|
package provide punk::layout [tcl::namespace::eval punk::layout { |
||||||
|
variable pkg punk::layout |
||||||
|
variable version |
||||||
|
set version 999999.0a1.0 |
||||||
|
}] |
||||||
|
return |
||||||
|
|
||||||
@ -0,0 +1,3 @@ |
|||||||
|
0.1.0 |
||||||
|
#First line must be a semantic version number |
||||||
|
#all other lines are ignored. |
||||||
@ -1,3 +1,3 @@ |
|||||||
0.1.4 |
0.1.5 |
||||||
#First line must be a semantic version number |
#First line must be a semantic version number |
||||||
#all other lines are ignored. |
#all other lines are ignored. |
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,446 @@ |
|||||||
|
# -*- tcl -*- |
||||||
|
# Maintenance Instruction: leave the 999999.xxx.x as is and use punkshell 'dev make' or bin/punkmake to update from <pkg>-buildversion.txt |
||||||
|
# module template: shellspy/src/decktemplates/vendor/punk/modules/template_module-0.0.4.tm |
||||||
|
# |
||||||
|
# Please consider using a BSD or MIT style license for greatest compatibility with the Tcl ecosystem. |
||||||
|
# Code using preferred Tcl licenses can be eligible for inclusion in Tcllib, Tklib and the punk package repository. |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
# (C) 2025 |
||||||
|
# |
||||||
|
# @@ Meta Begin |
||||||
|
# Application punk::layout 0.1.0 |
||||||
|
# Meta platform tcl |
||||||
|
# Meta license MIT |
||||||
|
# @@ Meta End |
||||||
|
|
||||||
|
|
||||||
|
#EXPERIMENTAL |
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
## Requirements |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
|
||||||
|
|
||||||
|
package require Tcl 8.6- |
||||||
|
package require punk::args |
||||||
|
|
||||||
|
#experimental layout library based on Nic Barker's Clay |
||||||
|
# CLAY(CLAY_ID("parent"), { .layout = { .padding = CLAY_PADDING_ALL(8) } }) { |
||||||
|
# // Child element 1 |
||||||
|
# CLAY_TEXT(CLAY_STRING("Hello World"), CLAY_TEXT_CONFIG({ .fontSize = 16 })); |
||||||
|
# // Child element 2 with red background |
||||||
|
# CLAY(CLAY_ID("child"), { .backgroundColor = COLOR_RED }) { |
||||||
|
# // etc |
||||||
|
# } |
||||||
|
# } |
||||||
|
|
||||||
|
#Tcl translation? |
||||||
|
#CLAY ?-opt <val>..? ?childblock? |
||||||
|
#eg |
||||||
|
#CLAY -id <string> -layout <dict> -rectangle <dict> { childblock } |
||||||
|
#where childblock is curly braced code block |
||||||
|
|
||||||
|
# clay -id "parent" -layout [dict create .padding [padding_all 8]] { |
||||||
|
# clay_text "Hello World" [clay_textconfig {.fontsize 16}] |
||||||
|
# clay -id "child" -config {.backgroundColor red} { |
||||||
|
# # etc |
||||||
|
# } |
||||||
|
# } |
||||||
|
|
||||||
|
#for padding on a terminal - we are limited by cell_size - which is not square. (and may vary on terminals??) |
||||||
|
# e.g punk::console::cell_size returns cell wxh |
||||||
|
# -> 10x20 |
||||||
|
# (either queried from terminal - or defaults to 10x20 if can't be queried) |
||||||
|
#so the minimum 'even' padding we can have is 2 space for horizontal, 1 line vertical |
||||||
|
#we can pad a minimum of 1 space horizontall (10 units) and a minimum of 1 line vertically (20 units) |
||||||
|
# CLAY_PADDING {<l> <r> <t> <b>} |
||||||
|
#how then should we handle .padding {16 16 8 8} - quantize by roundup to h 10 and v 20? |
||||||
|
# -> {20 20 20 20} ? |
||||||
|
# or round to nearest vertical and horizontal quanta? |
||||||
|
# -> {20 20 0 0} |
||||||
|
#either way - {16 16 16 16} goes to {20 20 20 20} - which is *close* to equal v h padding |
||||||
|
# - but not perfect visually because fonts within a cell have baseline, topline etc internal padding too? |
||||||
|
|
||||||
|
#building a ui ? |
||||||
|
#clay_begin_layout |
||||||
|
# // build UI here |
||||||
|
#set rendercommands [clay_end_layout] |
||||||
|
#some_render_command $rendercommands |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#eg2 (from introducing clay video https://www.youtube.com/watch?v=DYWTw19_8r4&t=2s) |
||||||
|
#something like the following? |
||||||
|
# (stopped at around the scroll point - need to work out console mouse features. punk::console::mouse_enable ...) |
||||||
|
|
||||||
|
#extracted out common layout 'sizing' for grow in both directions |
||||||
|
#set layoutExpand { |
||||||
|
# width CLAY_SIZING_GROW |
||||||
|
# heigth CLAY_SIZING GROW |
||||||
|
# } |
||||||
|
#extracted out common background panel |
||||||
|
#set contentBackgroundConfig { |
||||||
|
# colour Term-grey |
||||||
|
# frametype arc |
||||||
|
# } |
||||||
|
#proc RenderHeaderButton {} {} ;#?? |
||||||
|
|
||||||
|
#set documents [dict create\ |
||||||
|
# Squirrels "The secret life of Squirrels"\ |
||||||
|
# "Lorem Ipsum" "Orem ipsum dolor sit ..\netc blah"\ |
||||||
|
# "Vacuum instructions" "Chapter 3: Getting Started"\ |
||||||
|
# "Article 4" "Article 4"\ |
||||||
|
# "Article 5" "Article 5"\ |
||||||
|
# ] |
||||||
|
|
||||||
|
#set selectedDocumentIndex 0 |
||||||
|
|
||||||
|
|
||||||
|
#clay_begin_layout |
||||||
|
#clay -id "OuterContainer"\ |
||||||
|
# -item [clay_rectangle {colour web-red}] |
||||||
|
# -layout [clay_layout [dict create\ |
||||||
|
# layoutDirection CLAY_TOP_TO_BOTTOM\ |
||||||
|
# sizing $layoutExpand\ |
||||||
|
# padding {16 16}\ |
||||||
|
# childGap 16\ |
||||||
|
# ]]\ |
||||||
|
# { |
||||||
|
# # Child elements |
||||||
|
# clay -id "HeaderBar"\ |
||||||
|
# -item [clay_rectangle $contentBackgroundConfig]\ |
||||||
|
# -layout [clay_layout { |
||||||
|
# sizing { |
||||||
|
# height 60 |
||||||
|
# width CLAY_SIZING_GROW |
||||||
|
# } |
||||||
|
# padding {8 8} |
||||||
|
# childGap 8 |
||||||
|
# childAlignment { |
||||||
|
# y CLAY_ALIGN_Y_CENTER |
||||||
|
# } |
||||||
|
# }]\ |
||||||
|
# { |
||||||
|
# # Header buttons go here |
||||||
|
# RenderheaderButton [clay_string "File"] |
||||||
|
# RenderheaderButton [clay_string "Edit"] |
||||||
|
# #push last 3 buttons to rhs by using a layout in between to take up the middle space |
||||||
|
# CLAY -layout {sizing {width CLAY_SIZING_GROW}} {} |
||||||
|
# RenderheaderButton [clay_string "Upload"] |
||||||
|
# RenderheaderButton [clay_string "Media"] |
||||||
|
# RenderheaderButton [clay_string "Support"] |
||||||
|
# } |
||||||
|
# clay -id "LowerContent"\ |
||||||
|
# -layout [dict create sizing $layoutExpand childGap 16]\ |
||||||
|
# { |
||||||
|
# clay -id "Sidebar"\ |
||||||
|
# -item [clay_rectangle $contentBackgroundConfig]\ |
||||||
|
# -layout { |
||||||
|
# layoutDirection CLAY_TOP_TO_BOTTOM |
||||||
|
# padding {8 8} |
||||||
|
# childgap {20} |
||||||
|
# sizing { |
||||||
|
# width = 60 |
||||||
|
# height = CLAY_SIZING_GROW |
||||||
|
# }\ |
||||||
|
# }\ |
||||||
|
# { |
||||||
|
# #render dynamic data |
||||||
|
# dict for {title data} $documents { |
||||||
|
# clay -layout {padding {8 8}\ |
||||||
|
# { |
||||||
|
# #fontId, fontSize not really practical on terminal |
||||||
|
# clay_text $title [clay_text_config { |
||||||
|
# textColor cyan |
||||||
|
# }] |
||||||
|
# } |
||||||
|
# } |
||||||
|
# } |
||||||
|
# clay -id "MainContent"\ |
||||||
|
# -item [clay_rectangle $contentBackgroundConfig]\ |
||||||
|
# -scroll {vertical true}\ |
||||||
|
# -layout [dict create\ |
||||||
|
# layoutDirection = CLAY_TOP_TO_BOTTOM\ |
||||||
|
# childGap 20\ |
||||||
|
# padding {16 16 20 20}\ |
||||||
|
# sizing $layoutExpand\ |
||||||
|
# ]\ |
||||||
|
# { |
||||||
|
# set selectedTitle [lindex [dict keys $documents] $selectedDocumentIndex] |
||||||
|
# clay_text $selectedTitle [clay_text_config { |
||||||
|
# textcolour {web-white bold} |
||||||
|
# }] |
||||||
|
# clay_text [dict get $documents $selectedTitle] [clay_text_config { |
||||||
|
# textcolour {web-white} |
||||||
|
# }] |
||||||
|
# } |
||||||
|
# } |
||||||
|
# } |
||||||
|
#set rendercommands [clay_end_layout] |
||||||
|
|
||||||
|
#CLAY_SIZING_GROW |
||||||
|
#CLAY_SIZING_FIT ;#default |
||||||
|
|
||||||
|
tcl::namespace::eval punk::layout { |
||||||
|
variable PUNKARGS |
||||||
|
namespace eval argdoc { |
||||||
|
variable PUNKARGS |
||||||
|
namespace eval argdoc { |
||||||
|
#non-colour SGR codes |
||||||
|
set I "\x1b\[3m" ;# [a+ italic] |
||||||
|
set NI "\x1b\[23m" ;# [a+ noitalic] |
||||||
|
set B "\x1b\[1m" ;# [a+ bold] |
||||||
|
set N "\x1b\[22m" ;# [a+ normal] |
||||||
|
set T "\x1b\[1\;4m" ;# [a+ bold underline] |
||||||
|
set NT "\x1b\[22\;24m\x1b\[4:0m" ;# [a+ normal nounderline] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#elementDeclaration |
||||||
|
|
||||||
|
#data structure (CLAY UIElement struct equivalent) |
||||||
|
#set eg_element [dict create\ |
||||||
|
# position {1 1}\ |
||||||
|
# size {80 24}\ |
||||||
|
# children [list]\ |
||||||
|
#] |
||||||
|
|
||||||
|
|
||||||
|
#elementConfig |
||||||
|
#set eg_item [dict create\ |
||||||
|
# type rectangle\ |
||||||
|
# color {red bold}\ |
||||||
|
# borderColor {yellow} |
||||||
|
# frametype {}\ |
||||||
|
#] |
||||||
|
# etc |
||||||
|
proc DrawRectangle {element} { |
||||||
|
set position [dict get $element position] ;#x y |
||||||
|
set size [dict get $element size] ;#width height |
||||||
|
lassign $size w h |
||||||
|
set bg [dict get $element color] |
||||||
|
set borderColor [dict get $element borderColor] |
||||||
|
set frametype [dict get $element frametype] |
||||||
|
if {$frametype eq ""} { |
||||||
|
#much faster than textblock::frame |
||||||
|
set content [textblock::block $w $h "[a+ $bg] [a]"] |
||||||
|
} else { |
||||||
|
#slow - and frame background colours will always overlap the borders so we can never get a nicely filled arc frame for example |
||||||
|
#The best bordered |
||||||
|
set content [textblock::frame -type $frametype -ansibase [a+ $bg] -ansiborder [a+ $borderColor] -width $w -height $h " "] |
||||||
|
} |
||||||
|
} |
||||||
|
proc RenderElement {element} { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#TODO |
||||||
|
#The clay_begin_layout - clay_end_layout structure suggests the main API may be better represented as an oo object maintaining state |
||||||
|
#we may need to have multiple instances running concurrently |
||||||
|
#Clay conceptually runs in 'immediate' mode - ie recalculating each full layout each time we need to render |
||||||
|
|
||||||
|
variable rendercommands |
||||||
|
proc clay_begin_layout {} { |
||||||
|
variable rendercommands |
||||||
|
set rendercommands [dict create] |
||||||
|
#...? |
||||||
|
} |
||||||
|
proc clay_end_layout {} { |
||||||
|
variable rendercommands |
||||||
|
|
||||||
|
#...? |
||||||
|
|
||||||
|
return $rendercommands |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
variable PUNKARGS |
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id ::punk::layout::clay |
||||||
|
@cmd -name punk::layout::clay |
||||||
|
@opts |
||||||
|
-id -type string |
||||||
|
-item -type dict |
||||||
|
-layout -type dict |
||||||
|
-backgroundColor -type list -help\ |
||||||
|
"ANSI colour codes as documented in punk::ansi::a?" |
||||||
|
@values -min 0 -max 1 |
||||||
|
childscript -type script |
||||||
|
}] |
||||||
|
} |
||||||
|
#For initial proof of concept - we will use punk::args for *parsing* as well as documenting |
||||||
|
#For final version - the extra overhead of punk::args::parse may not be suitable for large numbers of |
||||||
|
#calls in 'immediate' mode - so punk::args::parse should be used on 'unhappy' paths only, |
||||||
|
#with a fast switch statement used for actual option parsing. |
||||||
|
#As performance is likely to be an issue - we should also avoid allowing 'prefixes' for arguments |
||||||
|
proc clay {args} { |
||||||
|
set argd [punk::args::parse $args withid ::punk::layout::clay] ;#temporary during concept development - todo: change to manual parsing for performance. |
||||||
|
|
||||||
|
} |
||||||
|
# |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
# Secondary API namespace |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
tcl::namespace::eval punk::layout::lib { |
||||||
|
tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase |
||||||
|
tcl::namespace::path [tcl::namespace::parent] |
||||||
|
|
||||||
|
|
||||||
|
#STEPS |
||||||
|
#Fit Sizing -> Grow Sizing -> Positions -> Draw |
||||||
|
#tree traversal |
||||||
|
#(reverse breadth first) (breadth first) |
||||||
|
# via OpenElement/CloseElement? (expand our grow containers |
||||||
|
# into any available space) |
||||||
|
|
||||||
|
proc OpenElement {element} { |
||||||
|
#pseudo... |
||||||
|
#set layoutDirection $layoutconfig.layoutDirection |
||||||
|
#if {$layoutDirection eq "CLAY_LEFT_TO_RIGHT"} { |
||||||
|
#} else { |
||||||
|
# #CLAY_TOP_TO_BOTTOM |
||||||
|
#} |
||||||
|
} |
||||||
|
proc CloseElement {element} { |
||||||
|
#element.parent.width += element.width |
||||||
|
#element.parent.height += element.height |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
proc SizeContainersAlongAxis {is_x_axis totalSizeToDistribute} { |
||||||
|
} |
||||||
|
proc CalculateFinalLayout {} { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#tcl::namespace::eval punk::layout::system { |
||||||
|
#} |
||||||
|
|
||||||
|
|
||||||
|
# == === === === === === === === === === === === === === === |
||||||
|
# Sample 'about' function with punk::args documentation |
||||||
|
# == === === === === === === === === === === === === === === |
||||||
|
tcl::namespace::eval punk::layout { |
||||||
|
tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase |
||||||
|
variable PUNKARGS |
||||||
|
variable PUNKARGS_aliases |
||||||
|
|
||||||
|
lappend PUNKARGS [list { |
||||||
|
@id -id "(package)punk::layout" |
||||||
|
@package -name "punk::layout" -help\ |
||||||
|
"EXPERIMENTAL - not done" |
||||||
|
}] |
||||||
|
|
||||||
|
namespace eval argdoc { |
||||||
|
#namespace for custom argument documentation |
||||||
|
proc package_name {} { |
||||||
|
return punk::layout |
||||||
|
} |
||||||
|
proc about_topics {} { |
||||||
|
#info commands results are returned in an arbitrary order (like array keys) |
||||||
|
set topic_funs [info commands [namespace current]::get_topic_*] |
||||||
|
set about_topics [list] |
||||||
|
foreach f $topic_funs { |
||||||
|
set tail [namespace tail $f] |
||||||
|
lappend about_topics [string range $tail [string length get_topic_] end] |
||||||
|
} |
||||||
|
#Adjust this function or 'default_topics' if a different order is required |
||||||
|
return [lsort $about_topics] |
||||||
|
} |
||||||
|
proc default_topics {} {return [list Description *]} |
||||||
|
|
||||||
|
# ------------------------------------------------------------- |
||||||
|
# get_topic_ functions add more to auto-include in about topics |
||||||
|
# ------------------------------------------------------------- |
||||||
|
proc get_topic_Description {} { |
||||||
|
punk::args::lib::tstr [string trim { |
||||||
|
package punk::layout |
||||||
|
An experiment in using CLAY UI style layout on the terminal |
||||||
|
INCOMPLETE... |
||||||
|
status: notes and started framework only - nothing usable. |
||||||
|
} \n] |
||||||
|
} |
||||||
|
proc get_topic_License {} { |
||||||
|
return "MIT" |
||||||
|
} |
||||||
|
proc get_topic_Version {} { |
||||||
|
return "$::punk::layout::version" |
||||||
|
} |
||||||
|
proc get_topic_Contributors {} { |
||||||
|
set authors {{Julian Noble <julian@precisium.com.au}} |
||||||
|
set contributors "" |
||||||
|
foreach a $authors { |
||||||
|
append contributors $a \n |
||||||
|
} |
||||||
|
if {[string index $contributors end] eq "\n"} { |
||||||
|
set contributors [string range $contributors 0 end-1] |
||||||
|
} |
||||||
|
return $contributors |
||||||
|
} |
||||||
|
proc get_topic_concepts {} { |
||||||
|
punk::args::lib::tstr -return string { |
||||||
|
see Nic Barker's youtube video: How Clay's UI Layout Algorithm Works |
||||||
|
https://www.youtube.com/watch?v=by9lQvpvMIc |
||||||
|
} |
||||||
|
} |
||||||
|
# ------------------------------------------------------------- |
||||||
|
} |
||||||
|
|
||||||
|
# we re-use the argument definition from punk::args::standard_about and override some items |
||||||
|
set overrides [dict create] |
||||||
|
dict set overrides @id -id "::punk::layout::about" |
||||||
|
dict set overrides @cmd -name "punk::layout::about" |
||||||
|
dict set overrides @cmd -help [string trim [punk::args::lib::tstr { |
||||||
|
About punk::layout |
||||||
|
}] \n] |
||||||
|
dict set overrides topic -choices [list {*}[punk::layout::argdoc::about_topics] *] |
||||||
|
dict set overrides topic -choicerestricted 1 |
||||||
|
dict set overrides topic -default [punk::layout::argdoc::default_topics] ;#if -default is present 'topic' will always appear in parsed 'values' dict |
||||||
|
set newdef [punk::args::resolved_def -antiglobs -package_about_namespace -override $overrides ::punk::args::package::standard_about *] |
||||||
|
lappend PUNKARGS [list $newdef] |
||||||
|
proc about {args} { |
||||||
|
package require punk::args |
||||||
|
#standard_about accepts additional choices for topic - but we need to normalize any abbreviations to full topic name before passing on |
||||||
|
set argd [punk::args::parse $args withid ::punk::layout::about] |
||||||
|
lassign [dict values $argd] _leaders opts values _received |
||||||
|
punk::args::package::standard_about -package_about_namespace ::punk::layout::argdoc {*}$opts {*}[dict get $values topic] |
||||||
|
} |
||||||
|
} |
||||||
|
# end of sample 'about' function |
||||||
|
# == === === === === === === === === === === === === === === |
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------- |
||||||
|
# register namespace(s) to have PUNKARGS,PUNKARGS_aliases variables checked |
||||||
|
# ----------------------------------------------------------------------------- |
||||||
|
# variable PUNKARGS |
||||||
|
# variable PUNKARGS_aliases |
||||||
|
namespace eval ::punk::args::register { |
||||||
|
#use fully qualified so 8.6 doesn't find existing var in global namespace |
||||||
|
lappend ::punk::args::register::NAMESPACES ::punk::layout |
||||||
|
} |
||||||
|
# ----------------------------------------------------------------------------- |
||||||
|
|
||||||
|
# ++ +++ +++ +++ +++ +++ +++ +++ +++ +++ +++ |
||||||
|
## Ready |
||||||
|
package provide punk::layout [tcl::namespace::eval punk::layout { |
||||||
|
variable pkg punk::layout |
||||||
|
variable version |
||||||
|
set version 0.1.0 |
||||||
|
}] |
||||||
|
return |
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue