colintu has asked for the wisdom of the Perl Monks concerning the following question:

I have a Text widget in which I need to mark 'invalid' characters by changing their colour. This works except for two cases:

If the char is a newline/return then the whole of the rest of the line in the Text widget has it's colour changed rather than just the single char position after the last word.

I am highlighting space characters with a tag. When the text widget word wraps the text all of the remainder of the line is highlighted as if there were multiple spaces filling the end of the line.

It appears that the text widget is treating all of the white space displayed at the end of each word wrapped line as a single character.

Is this expected? Is this a bug? Is there a way of preventing this?

Colin

Replies are listed 'Best First'.
Re: Tags in TK Text widgets
by tybalt89 (Monsignor) on Aug 17, 2024 at 20:13 UTC

    It's not a bug, it's a feature :)

    A newline is not a displayable character, it's a repositioning of other displayable characters.
    The more I look at it, the more I think it's the correct way to display a newline, and even spaces occurring at the wrap point in a long line.

      The problem in this case is that the newline character is invalid input and needs to be highlighted so the user can see their error and remove it just like any other invalid character.

      Some background, this is part of a Morse code teaching system and for beginners the character set is purposefully kept small. Morse code does not have the concept of 'newline' and there is no code for such a character, so newline characters are invalid and need to be clearly marked as such.

      So, I need a way to make it obvious what the character is and having the Text widget highlight the rest of an apparently empty line is not optimal.

      So I need a way to replace the newline characters with something that will show up clearly.

        One thing to do is to replace newlines with spaces in the validate sub.

        sub validate_text { my $wstring = $ts_box->get("1.0", "end") =~ s/\s+/ /gr; $ts_box->delete('1.0', 'end'); $ts_box->insert('end', $wstring); $ts_box->tagAdd( 'badc', "1.$-[0]", "1.$+[0]" ) while $wstring =~ /[^A-Za-z0-9 \/.,=?*+]+/g; }

        Some of the existing code has been slightly tightened up, just a little. :)

        You say: "So I need a way to replace the newline characters with something that will show up clearly."

        The obvious thing to do would be to show what the Morse code guy would send in place of "\n", which would be the prosign {BT}. I used enclosing curly brackets to designate a prosign. When writing a prosign by hand, there would be a line atop the characters "BT". Instead of BT, move the underline to on top of the characters. If you send the characters: "BT", there will be 3 dots worth of time spacing between the B and the T. When sent as a prosign, there is no spacing and the two characters are run together. When someone runs characters together unintentionally, they are known as a "swing fist" and it is very hard to understand them!

        Samuel Morse's historic message would have been sent: "WHAT HATH GOD WROUGHT {SK}", the prosign SK means the end of the message. There are a bunch of common prosigns: {BT} (change of thought/paragraph), {AS} (standby). {AA} (All After). {BK} ("over to you") among them.

        You say you are teaching beginners. I would not teach the weirdo math characters +-*= as a first pass. I suggest you come up with a list of prosigns to teach - these will be heard far more often than these "math" characters. Another issue is how to represent the Error symbol. That is 8 dits in a row. I've never written a character for that because this is the "oops" character when you have botched the spelling of a word or other error. But I suppose if this is some test lesson script, you would want to include that so the students will know how to deal with it when they hear it.

        For other UI possibilities, I think it would be possible to have the input refuse to enter any illegal character. Perhaps it looks to the user like nothing happens when he types "&". Some prototyping and extra thought are required to see if that idea pans out.

        I did run your code. I had to install the TFrame module. For this demo, Frame would have sufficed. I see weird spaces in a number of places on my display. Like extra space before the highlighted "&" or when a space character precedes a "bad" character. This extra space is not composed of space characters since a "copy-n-paste" does not copy any extra spaces. I am puzzled by this behavior and I don't know if it is because of the ancient version of Perl that I run (5.24). I am still fiddling with a few ideas.

        I suppose you could be teaching Morse as part of an HST (High Speed Telegraphy) competition? If so, I would read the rules on the universe of symbols that could be sent.

        Update: Oh not all prosigns are 2 letters, {AAA} is a "full stop". Some old guy on the Titanic would have been familiar with that, but I don't hear that. Update- that's not right - just forget it.

Re: Tags in TK Text widgets
by Marshall (Canon) on Aug 14, 2024 at 19:13 UTC
    I am not sure exactly what you are doing. Please post a short runnable piece of code that illustrates your issues.

      Code below, press the validate button to see the problem with line wrap and 'newline chars' as per my original post.

      #!/usr/bin/perl # #Text widget word wrap problem demo # use strict; use Tk; use Tk::TFrame; my $mw = MainWindow->new; my $big_font = $mw->fontCreate( -size => 11, -family => 'Terminal', -weight => 'bold'); $mw->title("Demo App"); my $seg1_f = $mw->TFrame(-label => 'Segment 1', -relief => 'groove', - +borderwidth => 2); my $ts_box = $seg1_f->Text(-wrap => 'word', -width => 60, -height => +6, -font => $big_font); $ts_box->grid( -padx => 4, -pady => 4, -row => 0, -column => 4, -sticky => "nsew", -rowspan => + 4); $ts_box->menu(undef); $ts_box->tagConfigure("badc", -foreground => "red", -background => 'ye +llow'); my $val = $seg1_f->Button(-text => "Validate", -command => [\&valida +te_text, 4]); $val->grid(-row => 0, -column => 3, -padx => 4, -pady => 5); $seg1_f->grid(-row => 0, -column => 0, -sticky => "nsew",-padx => 2, - +pady => 2); $mw->resizable(0,0); $ts_box->insert('end', "This is some test text with some invalid @ cha +racters"); $ts_box->insert('end', " If you click the Validate button the bad char +s &should be"); $ts_box->insert('end', " highlighted! Note that a newline character"); $ts_box->insert('end', "\n hightlights to the end of the line not just + the single"); $ts_box->insert('end', " character position. Is this a bug?"); MainLoop; # Validate the text field sub validate_text{ my $i_st; my $idx; my $cnt; my $ch; $ts_box->tagRemove('badc', '1.0', 'end'); my $wstring = $ts_box->get("1.0", "end"); $idx = length($wstring)-1; if ($idx == 0) {return(0)} $cnt = 0; while ($idx != $cnt) { $ch = substr($wstring, $cnt, 1); if (!($ch =~ /[A-Za-z0-9 \/.,=?*+]/)) { $i_st = sprintf("1.%d", $cnt); # bad char $ts_box->tagAdd('badc', $i_st); } $cnt++; } }
        The behaviour doesn't need a Tk::TFrame, it stays the same using Tk::Frame, too.

        Moreover, it seems nothing is highlighted if the line ending in the newline has the maximum length, which would concern me much more. Maybe indicate the newlines by something (a pilcrow ¶ maybe?).

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
        Also note that nothing after a newline gets validated.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]