Revisiting this code by @MochiMoppel on the old forum.
https://oldforum.puppylinux.com/viewtop ... 4#p1037564
I have a use for it but using the arrow keys results in keycodes. How to correct?
I guess it need a case statement to translate all functional keys (arrows, tab, space ,Win, BackSpace, Return). I intend to type characters in a placeholder amidst markup formatting. So that the markup interpreter displays the effected text on the cairo canvas in either yad --text-info or yad --form --field=:txt
YAD - tips'n'tricks
Moderator: Forum moderators
- stemsee
- Posts: 830
- Joined: Sun Jul 26, 2020 8:11 am
- Location: lattitude 8
- Has thanked: 195 times
- Been thanked: 142 times
- Contact:
Re: YAD - tips'n'tricks
Re: YAD - tips'n'tricks
stemsee wrote: Tue Feb 25, 2025 8:28 amI have a use for it but using the arrow keys results in keycodes. How to correct?
Well, it can read the keys and skip them.
Code: Select all
while true ;do
IFS='' read -rsN 1 ound
if [[ $ound == $'\x1b' ]]; then
read -rsn2 -t 0.1 dummy && continue
fi
if [[ $ound == $'\x7f' ]] ;then
buffer=${buffer%?}
echo -ne "\b" >&2
else
buffer=$buffer$ound
echo -n "$ound" >&2
fi
echo -e "\f"
echo "$buffer"
done | yad --text-info --width=200 --height=200 --tail
---------------------------
I experimented a bit, suppressing contol combinations was tricky.
You may also need word wrapping or character wrapping.
This is with word wrapping:
Code: Select all
#!/bin/bash
LINE_WIDTH=45 # Characters per line before wrapping
while true ;do
IFS='' read -rsN 1 ound
case $ound in
$'\x1b') # Handle escape sequences (like arrow keys)
read -rsn2 -t 0.1 input
continue
;;
$'\r'|$'\n') # Ignore Enter key
continue
;;
$'\x7f') # Handle Backspace
buffer=${buffer%?}
echo -ne "\b \b" >&2
;;
$'\t') # Ignore Tab key
continue
;;
$' ') # Handle Space (ensure it's visible)
buffer+=$' '
echo -n " " >&2
;;
$'\xE2') # Windows/Super key (part of multi-byte sequence, ignore)
read -rsn2 -t 0.1 input
continue
;;
$'\x00'|$'\x01'|$'\x02'|$'\x03'|$'\x04'|$'\x05'|$'\x06'|\
$'\x07'|$'\x08'|$'\x09'|$'\x0A'|$'\x0B'|$'\x0C'|$'\x0D'|\
$'\x0E'|$'\x0F'|$'\x10'|$'\x11'|$'\x12'|$'\x13'|$'\x14'|\
$'\x15'|$'\x16'|$'\x17'|$'\x18'|$'\x19'|$'\x1A'|$'\x1C'|\
$'\x1D'|$'\x1E'|$'\x1F') # Ignore all Ctrl key combinations
continue
;;
*)
buffer=$buffer$ound
echo -n "$ound" >&2
esac
# Word wrapping logic
formatted_buffer=""
current_line=""
for word in $buffer; do
if [[ $((${#current_line} + ${#word} + 1)) -gt $LINE_WIDTH ]]; then
formatted_buffer+="$current_line\n"
current_line="$word"
else
if [[ -z "$current_line" ]]; then
current_line="$word"
else
current_line+=" $word"
fi
fi
done
formatted_buffer+="$current_line"
echo -e "\f"
echo -e "$formatted_buffer"
done | yad --text-info --width=200 --height=200 --tail
For character wrapping you would do next
Code: Select all
# Format output with line wrapping
formatted_buffer=""
for ((i=0; i<${#buffer}; i+=LINE_WIDTH)); do
formatted_buffer+="${buffer:i:LINE_WIDTH}\n"
done
But I wanted to combine both methods:
By default it will apply word wrapping, if the word exceeds line width if will do character wrapping and go to next line.
Without further ado, here it is >
Code: Select all
#!/bin/bash
LINE_WIDTH=45 # Characters per line before wrapping
while true ;do
IFS='' read -rsN 1 ound
case $ound in
$'\x1b') # Handle escape sequences (like arrow keys)
read -rsn2 -t 0.1 input
continue
;;
$'\r'|$'\n') # Ignore Enter key
continue
;;
$'\x7f') # Handle Backspace
buffer=${buffer%?}
echo -ne "\b \b" >&2
;;
$'\t') # Ignore Tab key
continue
;;
$' ') # Handle Space (ensure it's visible)
buffer+=$' '
echo -n " " >&2
;;
$'\xE2') # Windows/Super key (part of multi-byte sequence, ignore)
read -rsn2 -t 0.1 input
continue
;;
$'\x00'|$'\x01'|$'\x02'|$'\x03'|$'\x04'|$'\x05'|$'\x06'|\
$'\x07'|$'\x08'|$'\x09'|$'\x0A'|$'\x0B'|$'\x0C'|$'\x0D'|\
$'\x0E'|$'\x0F'|$'\x10'|$'\x11'|$'\x12'|$'\x13'|$'\x14'|\
$'\x15'|$'\x16'|$'\x17'|$'\x18'|$'\x19'|$'\x1A'|$'\x1C'|\
$'\x1D'|$'\x1E'|$'\x1F') # Ignore all Ctrl key combinations
continue
;;
*)
buffer=$buffer$ound
echo -n "$ound" >&2
esac
# Format output with line wrapping
#formatted_buffer=""
# for ((i=0; i<${#buffer}; i+=LINE_WIDTH)); do
# formatted_buffer+="${buffer:i:LINE_WIDTH}\n"
#done
# Word + Character Wrapping Logic
formatted_buffer=""
current_line=""
for word in $buffer; do
if [[ ${#word} -gt $LINE_WIDTH ]]; then
# If a word is too long, break it into chunks of LINE_WIDTH
while [[ ${#word} -gt $LINE_WIDTH ]]; do
formatted_buffer+="${word:0:LINE_WIDTH}\n"
word="${word:LINE_WIDTH}"
done
current_line="$word"
elif [[ $((${#current_line} + ${#word} + 1)) -gt $LINE_WIDTH ]]; then
# Move to a new line when exceeding LINE_WIDTH
formatted_buffer+="$current_line\n"
current_line="$word"
else
# Append word to current line
if [[ -z "$current_line" ]]; then
current_line="$word"
else
current_line+=" $word"
fi
fi
done
formatted_buffer+="$current_line"
echo -e "\f"
echo -e "$formatted_buffer"
done | yad --text-info --width=200 --height=200 --tail
Stealing from the poor to give to the rich!
bslit - Block Splitter Custom Calendar Widget + Diary
youtu.be/O3FMV3iugeI
- stemsee
- Posts: 830
- Joined: Sun Jul 26, 2020 8:11 am
- Location: lattitude 8
- Has thanked: 195 times
- Been thanked: 142 times
- Contact:
Re: YAD - tips'n'tricks
thanks @superhik
I actually wanted the arrow keys to 'work' and also the return key.
Here is something along the lines of what I want. When typing from the terminal running the code it is formatting the text as expected but entering a return/newline exits the <span></span> formatting. I can pipe to yad --form --field=:txt --cycle-read and this dialogue is editable so i can directly input a carriage return/newline.
Code: Select all
FONTNAME='sans italic 18'
FCOL='blue'
while true ;do
IFS='' read -rsN 1 ound
case "$ound" in
$'\x1b') # Handle escape sequences (like arrow keys)
read -rsn2 -t 0.1 input
continue
;;
$'\x7f') # Handle Backspace
buffer=${buffer%?}
echo -ne "\b" >&2
;;
$'\r') buffer=${buffer%?}
echo -e "\n \n" >&2
;;
*) buffer="$buffer$ound"
echo -ne "$ound" >&2
;;
esac
echo -e "\f"
echo -e "<span font='$FONTNAME' fgcolor='$FCOL'>$buffer</span>"
done | yad --form --field=:txt --cycle-read
or
Code: Select all
FONTNAME='sans italic 18'
FCOL='blue'
while true ;do
IFS='' read -rsN 1 ound
case "$ound" in
$'\x1b') # Handle escape sequences (like arrow keys)
read -rsn2 -t 0.1 input
continue
;;
$'\x7f') # Handle Backspace
buffer=${buffer%?}
echo -ne "\b" >&2
;;
$'\r') buffer=${buffer%?}
echo -e "\n \n" >&2
;;
*) buffer="$buffer$ound"
echo -ne "$ound" >&2
;;
esac
echo -e "\f"
echo -e "<span font='$FONTNAME' fgcolor='$FCOL'>$buffer</span>"
done | yad --text-info --width=200 --height=200 --tail --enable-spell --formatted
Re: YAD - tips'n'tricks
stemsee wrote: Wed Feb 26, 2025 4:25 amthanks @superhik
I actually wanted the arrow keys to 'work' and also the return key.
Here is something along the lines of what I want. When typing from the terminal running the code it is formatting the text as expected but entering a return/newline exits the <span></span> formatting. I can pipe to yad --form --field=:txt --cycle-read and this dialogue is editable so i can directly input a carriage return/newline.
So you want to use pango markup.
Here's something to get you started.
Code: Select all
#!/bin/bash
buffer="" # The text buffer
cursor_pos=0 # The cursor position (0 = start)
while true; do
IFS='' read -rsN1 key
case "$key" in
$'\x1b') # Escape sequence: arrow keys
read -rsN2 -t 0.1 rest
case "$rest" in
"[C") # Right arrow
if (( cursor_pos < ${#buffer} )); then
((cursor_pos++))
fi
;;
"[D") # Left arrow
if (( cursor_pos > 0 )); then
((cursor_pos--))
fi
;;
esac
;;
$'\x7f') # Backspace
if (( cursor_pos > 0 )); then
buffer="${buffer:0:cursor_pos-1}${buffer:cursor_pos}" # Remove character at cursor position
((cursor_pos--))
fi
;;
$'\r'|$'\n') # Enter/Return: insert a newline into the buffer
buffer="${buffer:0:cursor_pos}$key${buffer:cursor_pos}" # Insert newline at cursor position
((cursor_pos++))
;;
*) # Regular character input
buffer="${buffer:0:cursor_pos}$key${buffer:cursor_pos}" # Insert character at cursor position
((cursor_pos++))
;;
esac
# Rebuild the styled output
styled_buffer=""
line_index=0 # Track line number
for ((i=0; i<${#buffer}; i++)); do
char="${buffer:$i:1}"
# Check if we encounter a newline character
if [[ "$char" == $'\n' ]]; then
line_index=$((line_index + 1)) # New line, increment line_index
styled_buffer+="$char"
else
# Apply styling for regular characters
if (( i == cursor_pos )); then
styled_buffer+="<span font_desc='Arial 14' foreground='red'>|</span>"
fi
styled_buffer+="<span font_desc='Arial 14' foreground='blue'>$char</span>"
fi
done
# If the cursor is at the end, append a red cursor marker
if (( cursor_pos == ${#buffer} )); then
styled_buffer+="<span font_desc='Arial 14' foreground='red'>|</span>"
fi
# Output the formatted text to yad
echo -e "\f" # Clear the output
echo "$styled_buffer"
done | yad --text-info --width=200 --height=200 --tail --enable-spell --formatted
Stealing from the poor to give to the rich!
bslit - Block Splitter Custom Calendar Widget + Diary
youtu.be/O3FMV3iugeI
- stemsee
- Posts: 830
- Joined: Sun Jul 26, 2020 8:11 am
- Location: lattitude 8
- Has thanked: 195 times
- Been thanked: 142 times
- Contact:
Re: YAD - tips'n'tricks
Thanks again @superhik
You make light work of it, clearly another expert to learn from!
If I may ask more of your skill, how to increment line_index with up and down arrows?
In fact right now I am using the up down arrows to change font and text color on the fly.
Code: Select all
"[A") export FONT="$(yad --font)"
;;
"[B") export FCOL="$(yad --color)"
;;
I would like to be able to begin a new styled_buffer concatenated with the existent one with the new font and color. Presently all existing text adopts the new font and color retrospectively. Just keeping the $styled_buffer contents and adding a new <span font construction to subsequent additions would be desirable.
Also we still use the terminal to input text but it is no longer visible in the terminal only in the gui. Is there a choice?
Re: YAD - tips'n'tricks
stemsee wrote: Thu Feb 27, 2025 5:01 amThanks again @superhik
You make light work of it, clearly another expert to learn from!
If I may ask more of your skill, how to increment line_index with up and down arrows?
In fact right now I am using the up down arrows to change font and text color on the fly.
Code: Select all
"[A") export FONT="$(yad --font)" ;; "[B") export FCOL="$(yad --color)" ;;
I would like to be able to begin a new styled_buffer concatenated with the existent one with the new font and color. Presently all existing text adopts the new font and color retrospectively. Just keeping the $styled_buffer contents and adding a new <span font construction to subsequent additions would be desirable.
Also we still use the terminal to input text but it is no longer visible in the terminal only in the gui. Is there a choice?
Should be this.
Code: Select all
#!/bin/bash
buffer="" # The raw text buffer
cursor_pos=0 # The cursor position (0 = start)
total_lines=0 # Track total number of lines
current_font="Arial 14" # Default font
current_color="blue" # Default color
styles=() # Array to store style for each character (font,color)
# Function to count lines and update total_lines
count_lines() {
total_lines=$(echo -e "$buffer" | wc -l)
}
# Function to get current line number of cursor
get_current_line() {
echo -e "${buffer:0:cursor_pos}" | wc -l
}
# Function to update terminal display
update_terminal() {
tput clear >&2
local before_cursor="${buffer:0:cursor_pos}"
local after_cursor="${buffer:cursor_pos}"
echo -ne "${before_cursor}|${after_cursor}" >&2
local lines_before=$(echo -e "$before_cursor" | wc -l)
local chars_last_line=$(echo -e "$before_cursor" | tail -n1 | wc -c)
tput cup $((lines_before-1)) $((chars_last_line-1)) >&2
}
# Function to change font and color
change_style() {
current_font="$1"
current_color="$2"
}
# Function to build styled display buffer
build_display_buffer() {
local display_buffer=""
local i=0
while (( i < ${#buffer} )); do
if (( i == cursor_pos )); then
display_buffer="${display_buffer}<span font_desc=\"Arial 14\" foreground=\"red\">|</span>"
fi
local char="${buffer:$i:1}"
local style="${styles[$i]:-$current_font,$current_color}" # Default to current if unset
local font="${style%%,*}"
local color="${style#*,}"
if [[ "$char" == $'\n' ]]; then
display_buffer="${display_buffer}\n"
else
char="$(echo "$char" | sed -E 's/&/\&/g; s/</\</g; s/>/\>/g')" # Escape special characters
display_buffer="${display_buffer}<span font_desc=\"${font}\" foreground=\"${color}\">${char}</span>"
fi
((i++))
done
if (( cursor_pos == ${#buffer} )); then
display_buffer="${display_buffer}<span font_desc=\"Arial 14\" foreground=\"red\">|</span>"
fi
echo "$display_buffer"
}
while true; do
IFS='' read -rsN1 key
case "$key" in
$'\x1b') # Escape sequence: arrow keys
read -rsN2 -t 0.1 rest
case "$rest" in
"[C") # Right arrow
if (( cursor_pos < ${#buffer} )); then
((cursor_pos++))
fi
;;
"[D") # Left arrow
if (( cursor_pos > 0 )); then
((cursor_pos--))
fi
;;
"[A") # Up arrow
if (( $(get_current_line) > 1 )); then
current_line=$(get_current_line)
chars_before=$(echo -e "${buffer:0:cursor_pos}" | tail -n1 | wc -c)
prev_lines=$(echo -e "$buffer" | head -n $((current_line-1)))
prev_line_length=$(echo -e "$prev_lines" | tail -n1 | wc -c)
cursor_pos=$(echo -e "$prev_lines" | wc -c)
((cursor_pos--))
if (( chars_before - 1 < prev_line_length - 1 )); then
((cursor_pos -= (prev_line_length - chars_before)))
fi
fi
;;
"[B") # Down arrow
if (( $(get_current_line) < total_lines )); then
current_line=$(get_current_line)
chars_before=$(echo -e "${buffer:0:cursor_pos}" | tail -n1 | wc -c)
lines_before=$(echo -e "$buffer" | head -n $current_line | wc -c)
next_lines=$(echo -e "$buffer" | tail -n +$((current_line+1)))
next_line_length=$(echo -e "$next_lines" | head -n1 | wc -c)
cursor_pos=$lines_before
if (( chars_before - 1 < next_line_length - 1 )); then
((cursor_pos += chars_before - 1))
else
((cursor_pos += next_line_length - 1))
fi
fi
;;
esac
;;
$'\x7f') # Backspace
if (( cursor_pos > 0 )); then
buffer="${buffer:0:cursor_pos-1}${buffer:cursor_pos}"
for ((i=cursor_pos-1; i<${#styles[@]}-1; i++)); do
styles[$i]=${styles[$((i+1))]}
done
unset styles[${#styles[@]}-1]
((cursor_pos--))
fi
;;
$'\r'|$'\n') # Enter/Return
buffer="${buffer:0:cursor_pos}${key}${buffer:cursor_pos}"
for ((i=${#buffer}-1; i>cursor_pos; i--)); do
styles[$i]=${styles[$((i-1))]}
done
styles[cursor_pos]=""
((cursor_pos++))
;;
"!") # Change to grey Times
change_style "Times 14" "grey"
continue
;;
"@") # Change to green Courier
change_style "Courier 14" "green"
continue
;;
*) # Regular character input
buffer="${buffer:0:cursor_pos}${key}${buffer:cursor_pos}"
for ((i=${#buffer}-1; i>cursor_pos; i--)); do
styles[$i]=${styles[$((i-1))]}
done
styles[cursor_pos]="$current_font,$current_color"
((cursor_pos++))
;;
esac
# Update line count
count_lines
# Build and output styled display buffer
display_buffer="$(build_display_buffer)"
# Update terminal display
update_terminal
# Output to yad
echo -e "\f"
echo -e "$display_buffer"
done | yad --text-info --width=200 --height=200 --tail --enable-spell --formatted
Use "!" and "@" to change the color.
There is an array that stores style for each character, which may be slow it but could not make it work the other way.
Managing style tags is not that easy.
Sometimes up and down arrows are not positioning cursor correctly but I can't figure out why.
Stealing from the poor to give to the rich!
bslit - Block Splitter Custom Calendar Widget + Diary
youtu.be/O3FMV3iugeI
- stemsee
- Posts: 830
- Joined: Sun Jul 26, 2020 8:11 am
- Location: lattitude 8
- Has thanked: 195 times
- Been thanked: 142 times
- Contact:
Re: YAD - tips'n'tricks
IT works ... Many Thanks!
I have added a simple yad form to select font and color on '[' ... this way there are many fonts and colors to select. I will try to add underline, overline, rise and strikethrough to the style array.
Code: Select all
"[") # Change font and color
function markupfn {
SETS=$(yad --title="Formatted Preview" --text="$TXT" --form --field=:fn "sans 14" --field=text:clr "black" --field=back:clr "white" --field=underline:cb "none,single,double" --field=strikethru:chk "false" --field=rise:cb "superscript,subscript")
IFS='|' read -r font fcol bcol under strike rise<<<"$SETS"
export underline="$under"
export strike="$strike"
export rise="$rise"
change_style "$font" "$fcol"
}; export -f markupfn
markupfn
continue
;;
"]") # Change to black
change_style "Sans Bold 14" "black"
continue
;;
- stemsee
- Posts: 830
- Joined: Sun Jul 26, 2020 8:11 am
- Location: lattitude 8
- Has thanked: 195 times
- Been thanked: 142 times
- Contact:
Re: YAD - tips'n'tricks
I see what you mean about slowing down as the buffer content grows!
Better would be after each word instead of each character. This might be implemented by detecting word boundaries or spaces and substituting in the markup.
EDIT: Another approach might be to delete per character markup for sections with the same style, just keeping initial, <span font='' foreground=''> and end, </span> markup tags per group. This clean up would occur retrospectively with each newline character, and subsequently with each style change, changes acting upon the content of the buffer, or if the buffer is dumped to a file first then on that file.
- stemsee
- Posts: 830
- Joined: Sun Jul 26, 2020 8:11 am
- Location: lattitude 8
- Has thanked: 195 times
- Been thanked: 142 times
- Contact:
Re: YAD - tips'n'tricks
If i create a style_set array (which may be presets), which simply creates an indexed array of each successive change of style, with '</span>' being index 0
Code: Select all
# declare -a style_set=( '</span>' '<span font=\"${font[0]}\" foreground=\"${col[0]}>' '<span font=\"${font[1]}\" foreground=\"${col[1]}>' '<span font=\"${font[2]}\" foreground=\"${col[2]}>' '<span font=\"${font[3]}\" foreground=\"${col[3]}>' )
# echo "${style_set[0]}"
</span>
#
. Each successive style change denotes a 'section'. This way every change is a new section, even if it is a previously used style, it is still a new section. This array can be used with a regex argument to delete duplicate format strings per 'styled' word, and this will occur every time a $'\s' character is entered. There will also be a character counter so we know exactly how many duplicates of '<span font='' foreground=''>' and '</span>' strings to remove, -1, for each word, before resetting the character counter. There will also be a space counter so that after every nth space, excess format strings can again be culled.
- stemsee
- Posts: 830
- Joined: Sun Jul 26, 2020 8:11 am
- Location: lattitude 8
- Has thanked: 195 times
- Been thanked: 142 times
- Contact:
Re: YAD - tips'n'tricks
To do:
1) style_sets array
2) character counter which resets with space key entry.
3) remove duplicate markup strings per word on space key entry.
4) remove duplicate markup strings per section on style changes.