• Button1 vs Button3, same coordinates different lines

    From Luc@21:1/5 to All on Sun Dec 24 17:17:20 2023
    Merry Christmas, tclers!

    So here is my new problem.

    proc p.RunTime.MouseAction.button1 {widget} {
    puts "clicking button 1"
    lassign [winfo pointerxy $widget] x y
    # -- here be cheating, disabled --------
    # event generate $widget <Button-1> -x $x -y $y
    # ---------------------------------
    puts "b1 [winfo pointerxy $widget]"
    if {[catch {set firstmark [$widget index insert]}]} {return}
    lassign [split $firstmark "."] CURRLINE CURRCOL
    puts "b1 line $CURRLINE"
    }

    proc p.RunTime.MouseAction.button3 {widget} {
    puts "clicking button 3"
    lassign [winfo pointerxy $widget] x y
    puts "b3 [winfo pointerxy $widget]"
    # -- here be cheating, enabled --------
    event generate $widget <Button-1> -x $x -y $y
    # ---------------------------------
    if {[catch {set firstmark [$widget index insert]}]} {return}
    lassign [split $firstmark "."] CURRLINE CURRCOL
    puts "b3 line $CURRLINE"
    }

    The stdout output:

    clicking button 1
    b1 116 414
    b1 line 13
    clicking button 3
    b3 116 414
    clicking button 1
    b1 116 414
    b1 line 16
    b3 line 16


    There are two aspects to contemplate here:

    1. How do I make a right click capture the text widget's focus like the
    left click does, I mean, on the correct line? That answer alone would
    have saved me the trouble.

    2. The trouble is interesting in itself. Because I didn't know how to
    achieve what I really wanted, I cheated with event generate. But then the
    mouse pointer captures the focus three lines below and I have no idea why.

    Moreover, when I uncomment the commented out line in the first proc
    (and send an event generate for the first button inside a proc that is
    already triggered by the first button), I get this error:

    too many nested evaluations (infinite loop?)
    too many nested evaluations (infinite loop?)
    while executing
    "$w index @$x,$y"
    (procedure "TextClosestGap" line 2)
    invoked from within
    "TextClosestGap $w $x $y"
    (procedure "tk::TextSelectTo" line 5)
    invoked from within
    "tk::TextSelectTo .topw.outerframe.unipane.fileListPane.filenameframe.box 93 420"
    (command bound to event)


    Of course my code is very broken. No question about that. But I don't
    have any TextClosestGap and tk::TextSelectTo procs. Those are internal
    Tk procs. Is that supposed to happen? Could that be a bug?


    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Gerald Lester on Sun Dec 24 20:35:40 2023
    On Sun, 24 Dec 2023 14:59:09 -0600, Gerald Lester wrote:

    The bindings are not magical, do a bindtags on the widget to see the
    binding tags, then look on each of them to see what they have for enter, >leave, and the clicks.
    **************************

    I find this difficult.

    I have this protoype:

    set widget .w
    bind Changeclick <3> "event generate %W <Button-1>; break"
    pack [entry $widget -textvariable var]
    set var noclick
    # bindtags $widget [concat Changeclick [bindtags $widget]]
    bind $widget <1> {exit}
    bind $widget <3> {set var "B3"}


    Since the line that does the substitution is commented out, the
    application exits when I left-click it and the entry box shows "B3"
    when I right-click it. OK.

    After I uncomment that line, the application exits when I left-click it
    or right-click it. It works. It does change the right-click to a left
    click.

    So does it work in the large application? No.

    proc p.RunTime.MouseAction.button3 {widget} {
    bind Changeclick <3> "event generate %W <Button-1>; break"
    bindtags $widget [concat Changeclick [bindtags $widget]]
    puts "clicking button 3"
    if {[catch {set firstmark [$widget index insert]}]} {return}
    lassign [split $firstmark "."] CURRLINE CURRCOL
    puts "b3 line $CURRLINE"
    }

    The puts line works, but it still gets the wrong line because it won't
    get the text widget focus like button 1 does. In fact, I get an error
    if I remove the 'catch' line. That means it's not entering the text
    widget at all.

    Maybe text widgets require some additional step?

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Mon Dec 25 02:43:26 2023
    Luc <luc@sep.invalid> wrote:
    On Sun, 24 Dec 2023 14:59:09 -0600, Gerald Lester wrote:

    The bindings are not magical, do a bindtags on the widget to see the >>binding tags, then look on each of them to see what they have for enter, >>leave, and the clicks.
    **************************

    I find this difficult.

    It is not so difficult, but one does have to "poke around":

    $ rlwrap wish
    % text .t
    .t
    % bindtags .t
    .t Text . all
    % bind Text

    <Shift-Button-5> <Shift-Button-4> <Button-5> <Button-4>
    <Shift-MouseWheel> <MouseWheel> <B2-Motion> <Button-2> <Control-Key-h>
    <<TkAccentBackspace>> <<TkClearIMEMarkedText>> <<TkEndIMEMarkedText>>
    <<TkStartIMEMarkedText>> <Meta-Key-Delete> <Meta-Key-BackSpace>
    <Meta-Key-greater> <Meta-Key-less> <Meta-Key-f> <Meta-Key-d>
    <Meta-Key-b> <<Redo>> <<Undo>> <Control-Key-t> <Control-Key-o>
    <Control-Key-k> <Control-Key-d> <Key-KP_Enter> <Key-Escape>
    <Control-Key> <Meta-Key> <Alt-Key> <Key> <Key-Insert>
    <<PasteSelection>> <<Clear>> <<Paste>> <<Copy>> <<Cut>> <<SelectNone>>
    <<SelectAll>> <Shift-Key-Select> <Control-Shift-Key-space> <Key-Select>
    <Control-Key-space> <Key-BackSpace> <Key-Delete> <Key-Return>
    <Control-Key-i> <Control-Shift-Key-Tab> <Control-Key-Tab>
    <Shift-Key-Tab> <Key-Tab> <Control-Shift-Key-End> <Control-Key-End>
    <Control-Shift-Key-Home> <Control-Key-Home> <<SelectLineEnd>>
    <<LineEnd>> <<SelectLineStart>> <<LineStart>> <Control-Key-Next>
    <Control-Key-Prior> <Shift-Key-Next> <Key-Next> <Shift-Key-Prior>
    <Key-Prior> <<SelectNextPara>> <<SelectPrevPara>> <<SelectNextWord>>
    <<SelectPrevWord>> <<NextPara>> <<PrevPara>> <<NextWord>> <<PrevWord>>
    <<SelectNextLine>> <<SelectPrevLine>> <<SelectNextChar>>
    <<SelectPrevChar>> <<NextLine>> <<PrevLine>> <<NextChar>> <<PrevChar>>
    <Control-B1-Motion> <Double-Control-Button-1> <Control-Button-1>
    <ButtonRelease-1> <B1-Enter> <B1-Leave> <Triple-Shift-Button-1>
    <Double-Shift-Button-1> <Shift-Button-1> <Triple-Button-1>
    <Double-Button-1> <B1-Motion> <Button-1>

    % bind Text <Button-1>

    tk::TextButton1 %W %x %y
    %W tag remove sel 0.0 end

    % info body tk::TextButton1

    variable ::tk::Priv

    set Priv(selectMode) char
    set Priv(mouseMoved) 0
    set Priv(pressX) $x
    set anchorname [tk::TextAnchor $w]
    $w mark set insert [TextClosestGap $w $x $y]
    $w mark set $anchorname insert
    # Set the anchor mark's gravity depending on the click position
    # relative to the gap
    set bbox [$w bbox [$w index $anchorname]]
    if {$x > [lindex $bbox 0]} {
    $w mark gravity $anchorname right
    } else {
    $w mark gravity $anchorname left
    }
    focus $w
    if {[$w cget -autoseparators]} {
    $w edit separator
    }

    %

    And you see the code Tk runs for a "left click" on a text widget.

    If you want right click to act just like left click, then just do:

    bind Text <Button-3> {tk::TextButton1 %W %x %y; %W tag remove sel 0.0 end}

    And you have made a right click (button 3) act identically to a left
    click, on all text widgets in your app.


    Maybe text widgets require some additional step?

    Most likely. And if you'd post minimal code that we could run, but
    that exhibits the issue, we might be able to be more helpful.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Rich on Mon Dec 25 22:14:20 2023
    On Mon, 25 Dec 2023 02:43:26 -0000 (UTC), Rich wrote:

    I find this difficult.

    It is not so difficult, but one does have to "poke around":

    **************************

    Many thanks for your help again. It was fascinating to see how you
    inspect the innards of the machine. I'm just sorry I couldn't figure it
    out by myself. I feel stupid. But I'm always grateful for the education.

    As I like to say, Tcl is easy but I don't always understand Tk. It all
    looks like a big entanglement of strings to me. Each one can move many
    other strings which in turn move their on.

    Your guidance seems to have pointed me in the right direction. But I
    still can only go so far.

    I bound Ctrl-Left and Ctrl-Right to swtching tabs. It works except that Ctrl-Left makes the selections go up one line at every key press.
    Only Ctrl-Left. So I put your teachings into practice:

    bindtags $::Widgets_unipane.fileListPane.filenameframe.box
    foreach b [bind Text] {puts $b}

    and here it is, I'm sure these are the culprits:

    <Control-Key-Next>
    <Control-Key-Prior>

    puts [bind Text <Control-Prior>]

    %W xview scroll -1 page

    puts [bind Text <Control-Next>]

    %W xview scroll 1 page


    I gotta say, I kind of resent these default bindings. They get in the
    way often. I think we should have more "pure" or "agnostic" widgets.

    Anyway, I'm stumped again. How do I kill those? I googled and found
    people with more or less similar problems, but never a satisfactory
    answer.

    For example:

    "Removing a binding from one bindtag does not mean that the event will
    not be processed; a binding on another bindtag might still pick it up.
    Trying to hack around the other bindtags to fool the code is not going
    to be satisfactory. However, if a binding on one tag finishes with a
    break, it prevents further bindtags from being tried; it terminates
    processing early."

    That sounds terse.

    1. Is it doable or not? I don't think that is answered clearly.

    2. I am using break. But that is obviously not preventing the hard coded
    [bind Text <Control-Prior>] from kicking in.

    3. Even more confusing is the fact that [bind Text <Control-Prior>] is
    causing the "ghost" movement, but [bind Text <Control-Next>] is not.
    Why is that?

    There are 500 lines in that text widget and the selection/insert mark is
    near the top. It would make sense if it didn't move up and did move down
    since there are literally hundreds of lines worth of scrolling below the selection/insert mark but no room for the widget to scroll up.

    And they're both interfering with the same one proc.

    if {$action == "next"} {
    just "forget" one tab and pack another,
    nothing to do with moving the selection
    }
    if {$action == "previous"} {
    just "forget" one tab and pack another,
    nothing to do with moving the selection
    }

    Why do they behave differently?

    I know you want to see code and all of this will be published eventually,
    but I'm at 1,000 lines of code here and I can't really isolate
    particularly small parts. I just hope you can have a clue out of what
    I'm telling you so far. Can you?


    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Tue Dec 26 04:55:42 2023
    Luc <luc@sep.invalid> wrote:
    On Mon, 25 Dec 2023 02:43:26 -0000 (UTC), Rich wrote:

    I find this difficult.

    It is not so difficult, but one does have to "poke around":

    **************************

    Many thanks for your help again. It was fascinating to see how you
    inspect the innards of the machine. I'm just sorry I couldn't figure it
    out by myself. I feel stupid. But I'm always grateful for the education.

    You can learn a lot of those "inside information" bits by reading
    various articles on the Tcl Wiki: https://wiki.tcl-lang.org/

    As I like to say, Tcl is easy but I don't always understand Tk. It all
    looks like a big entanglement of strings to me. Each one can move many
    other strings which in turn move their on.

    Your guidance seems to have pointed me in the right direction. But I
    still can only go so far.

    I bound Ctrl-Left and Ctrl-Right to swtching tabs. It works except that Ctrl-Left makes the selections go up one line at every key press.
    Only Ctrl-Left. So I put your teachings into practice:

    bindtags $::Widgets_unipane.fileListPane.filenameframe.box
    foreach b [bind Text] {puts $b}

    and here it is, I'm sure these are the culprits:

    <Control-Key-Next>
    <Control-Key-Prior>

    puts [bind Text <Control-Prior>]

    %W xview scroll -1 page

    puts [bind Text <Control-Next>]

    %W xview scroll 1 page

    Rather than being sure, verify. Instrument the code and see if they
    are indeed the ones that fire:

    bind Text <Control-Prior> {puts stderr "<Control-Prior>" ; %W xview scroll -1 page}
    bind Text <Control-Next> {puts stderr "<Control-Next>" ; %W xview scroll 1 page}

    If they are the ones that fire, then you know they are the ones you
    have to handle to prevent what you are seeing from happening.

    I gotta say, I kind of resent these default bindings. They get in the
    way often. I think we should have more "pure" or "agnostic" widgets.

    Without the bindings, the widgets would do nothing at all for key
    presses or mouse clicks. So either they have "defaults" and then
    behave as expected when clicked/typed at, or they have no bindings and
    do nothing, and you have to supply every binding necessary to make them
    useful widgets.

    Anyway, I'm stumped again. How do I kill those?

    Assuming they *are* indeed the ones at fault, one way is to just remove
    them:

    bind Text <Control-Prior> ""
    bind Text <Control-Next> ""

    And see if the undesired behavior stops.

    I googled and found people with more or less similar problems, but
    never a satisfactory answer.

    For example:

    "Removing a binding from one bindtag does not mean that the event will
    not be processed; a binding on another bindtag might still pick it up.
    Trying to hack around the other bindtags to fool the code is not going
    to be satisfactory. However, if a binding on one tag finishes with a
    break, it prevents further bindtags from being tried; it terminates processing early."

    That sounds terse.

    1. Is it doable or not? I don't think that is answered clearly.

    The paragraph you quoted is saying it is not always as trivial as
    removing a binding. But until you've tested the simple case (remove
    the binding) and found it did not work, there's little point to being
    concerned with the more complex aspect.

    2. I am using break. But that is obviously not preventing the hard
    coded [bind Text <Control-Prior>] from kicking in.

    Where and how are you using break?

    3. Even more confusing is the fact that [bind Text <Control-Prior>] is causing the "ghost" movement, but [bind Text <Control-Next>] is not.
    Why is that?

    No idea, as I can't see what you refer to as "ghost movement". You
    stated the "selection moves up" but the bindings you focus on are
    scrolling the entire text. To me "selection moves" is a very different
    meaning from "text scrolls up one line". The first means that
    additional lines of text above the beginning of the start of the
    current selection become selected. The second simply means the whole
    window scrolls up by one line.

    Why do they behave differently?

    I can't say. I'm not even certain you are focusing on the right
    bindings yet. Try instrumenting them to see if they are the ones that
    fire. And if yes, try deleting them to see if that stops the unwanted
    action.

    I just hope you can have a clue out of what I'm telling you so far.
    Can you?

    Some, but much of it is pure guesswork.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From et99@21:1/5 to Luc on Wed Dec 27 21:49:45 2023
    On 12/25/2023 5:14 PM, Luc wrote:
    On Mon, 25 Dec 2023 02:43:26 -0000 (UTC), Rich wrote:

    I find this difficult.

    It is not so difficult, but one does have to "poke around":

    **************************

    Many thanks for your help again. It was fascinating to see how you
    inspect the innards of the machine. I'm just sorry I couldn't figure it
    out by myself. I feel stupid. But I'm always grateful for the education.

    As I like to say, Tcl is easy but I don't always understand Tk. It all
    looks like a big entanglement of strings to me. Each one can move many
    other strings which in turn move their on.


    If you want your own behavior, you can change the order in bindtags so you always get control first.

    I think if you want to be serious with tcl/tk, you need to build some development tools. The exercise will help you understand how tk works. Producing output of the info commands in a nicely formatted fashion makes things a lot easier. Develop good
    tools once, and have them for a lifetime. Source them in whenever you need them.

    Use a console tool on linux. Use tkcon, or you can just use console.tcl on the wiki (a console command for linux). Learn how to dis-assemble a proc or method, modify it, and "eval" it back at runtime, say adding a puts to the code. A good exercise is to
    write the dis-assemble using info args, default, and body on a procedure.

    Look into the trace command as well. You can do execution traces on whenever a command is executed, and in tk, every widget gets a new command.

    Look into [info frame] where you can look at the frames above to tell where something is called from. With uplevel you can get the context of the caller as well. I generally incorporate that with a trace. You can do a variable trace to find out where
    some variable is getting clobbered unexpectedly and who's the culprit.

    Many of these kinds of tools are found on the wiki. For example, RS has many such little tools.

    A good programmer is like a good mechanic, and every mechanic prizes his toolset.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to All on Fri Dec 29 14:01:59 2023
    On Wed, 27 Dec 2023 21:49:45 -0800, et99 wrote:

    I think if you want to be serious with tcl/tk, you need to build some >development tools.
    **************************

    I catch your drift. I had to write some kind of debugger thingy to help
    me understand some bug that was hard to find and driving me crazy.

    Well, it worked. I found and killed the bug.

    I'm always trying to learn, I do read the wiki and there aren't enough
    days in a lifetime to read every interesting piece of code that RS has
    shared, but I read it and I'm learning.

    I'm not sure how "serious" I am with tcl/tk. I mean, it's a hobby. I
    don't do it professionally. I don't think I could. That I write any code
    at all and it even works (!) is quite an accomplishment to me already.
    I have very few pleasures in this life ever and coding in Tcl is one of
    them. I guess I'm good.

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From et99@21:1/5 to Luc on Fri Dec 29 21:40:47 2023
    On 12/29/2023 9:01 AM, Luc wrote:
    On Wed, 27 Dec 2023 21:49:45 -0800, et99 wrote:

    I think if you want to be serious with tcl/tk, you need to build some
    development tools.
    **************************

    I catch your drift. I had to write some kind of debugger thingy to help
    me understand some bug that was hard to find and driving me crazy.

    Well, it worked. I found and killed the bug.

    I'm always trying to learn, I do read the wiki and there aren't enough
    days in a lifetime to read every interesting piece of code that RS has shared, but I read it and I'm learning.

    I'm not sure how "serious" I am with tcl/tk. I mean, it's a hobby. I
    don't do it professionally. I don't think I could. That I write any code
    at all and it even works (!) is quite an accomplishment to me already.
    I have very few pleasures in this life ever and coding in Tcl is one of
    them. I guess I'm good.

    Yeah, it's a hobby for me now too.

    What I really enjoy is building tools. One reason is you don't have to obey some project dictator's standards. You get to do whatever you like doing.

    Here's a little tidbit which lets me see what's going on, it works especially well with widgets. This is one reason I love tcl/tk.

    Put the below in a file, say foo.tcl, and:

    wish foo.tcl

    it can take 1 arg, a value 0..3 for the stack depth to show.

    Click in the entry widget and watch what comes out. There's a lot going on behind the scenes.




    proc instr {name {put 0} {dep 3}} {
    set ::__tracing(0000) "begin trace of: $name"
    rename ::$name ::theReal$name
    set ::instr_name $name
    set ::instr_put $put
    set ::instr_dep $dep
    proc ::$name args {
    set fr1 {??? ???}
    set fr2 {??? ???}
    set fr3 {??? ???}
    catch {set fr1 [info frame -1]}
    catch {set fr2 [info frame -2]}
    catch {set fr3 [info frame -3]}
    set data1 {}
    set data2 {}
    set data3 {}
    foreach {item wid} {type 10 proc 25 line 8 cmd -1} {
    set ${item}1 ---
    set ${item}2 ---
    set ${item}3 ---
    catch {set ${item}1 [dict get $fr1 ${item}]}
    catch {set ${item}2 [dict get $fr2 ${item}]}
    catch {set ${item}3 [dict get $fr3 ${item}]}
    append data1 " " [format %${wid}s [set ${item}1]]
    append data2 " " [format %${wid}s [set ${item}2]]
    append data3 " " [format %${wid}s [set ${item}3]]
    }
    set ar [string range [string map {"\n" "\u2423"} $args] 0 150]
    set d1 [string range [string map {"\n" "\u2423"} $data1] 0 150]
    set d2 [string range [string map {"\n" "\u2423"} $data2] 0 150]
    set d3 [string range [string map {"\n" "\u2423"} $data3] 0 150]
    if { $::instr_dep == 2 } {
    set t "$::instr_name $ar\n$d1\n$d2"
    } elseif { $::instr_dep == 1 } {
    set t "$::instr_name $ar\n$d1"
    } elseif { $::instr_dep == 0 } {
    set t "$::instr_name $ar"
    } else {
    set t "$::instr_name $ar\n$d1\n$d2\n$d3"
    }
    set ::__tracing([format %04d [incr ::sourceCount] ]) $t
    if { $::instr_put } {
    puts $t
    }
    tailcall ::theReal$::instr_name {*}$args
    }

    }

    proc main {args} {
    set ::etext hello
    vwait forever
    }
    entry .e -text hello -textvariable etext
    pack .e

    instr .e yes {*}$argv ;# widget, puts output, depth (3,2,1,0)


    main one two three

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)