• how to create a dialog that is hidden/shown rather thancreated/destroyed

    From Mark Summerfield@mark@qtrac.eu to comp.lang.tcl on Fri Jun 21 09:55:13 2024
    From Newsgroup: comp.lang.tcl

    Below is a tiny program.

    The "About" form is created and destroyed. So you can click About then
    close the About form, then repeat as often as you like.

    The "Options" form is supposed to be shown and hidden. But once you click
    it and then close it, the entire app is blocked. So clearly I'm doing something wrong.

    Here's the code using Tcl/Tk 9.0b2 on Linux.

    #!/usr/bin/env wish9

    if {[info exists env(TK_SCALING)]} { tk scaling $env(TK_SCALING) }

    proc prepare_modal {form} {
    wm withdraw $form
    wm attributes $form -type dialog
    wm transient $form .
    }

    proc show_modal {form {focus_widget ""}} {
    wm deiconify $form
    raise $form
    focus $form
    if {$focus_widget ne ""} {
    focus $focus_widget
    }
    catch {grab set $form}
    catch {tkwait visibility $form}
    }

    variable optionsForm

    proc main {} {
    wm title . "Dialog Tests"
    wm minsize . 240 240
    wm attributes . -type dialog
    ttk::button .optionsButton -text Options… -underline 1 \
    -command on_options
    ttk::button .aboutButton -text About -underline 1 -command on_about
    pack .optionsButton -side top
    pack .aboutButton -side top
    }

    proc on_options {} {
    if {![info exists ::optionsForm]} {
    puts "on_options init"
    set ::optionsForm [toplevel .optionsForm]
    prepare_modal .optionsForm
    wm protocol .optionsForm WM_DELETE_WINDOW {wm
    withdraw .optionsForm}
    ttk::label .optionsForm.label -text Options
    pack .optionsForm.label
    }
    puts on_options ;# TODO use show_modal hide/show
    show_modal $::optionsForm ;# TODO give focus widget
    }

    proc on_about {} {
    toplevel .aboutForm
    prepare_modal .aboutForm
    wm protocol .aboutForm WM_DELETE_WINDOW {destroy .aboutForm}
    ttk::label .aboutForm.label -text About
    pack .aboutForm.label
    show_modal .aboutForm
    }

    main
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mark Summerfield@mark@qtrac.eu to comp.lang.tcl on Fri Jun 21 11:49:54 2024
    From Newsgroup: comp.lang.tcl

    I figured it out.

    I replaced this:

    catch { tkwait visibility $form }

    with

    catch {
    tkwait visibility $form
    grab release $form
    }

    and now it works.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mark Summerfield@mark@qtrac.eu to comp.lang.tcl on Fri Jun 21 11:58:56 2024
    From Newsgroup: comp.lang.tcl

    On Fri, 21 Jun 2024 11:49:54 +0000, Mark Summerfield wrote:

    I figured it out.

    I replaced this:

    catch { tkwait visibility $form }

    with

    catch {
    tkwait visibility $form grab release $form
    }

    and now it works.

    Actually, it doesn't work. Firstly it stops the Options form from being
    modal. Secondly, if you leave it up you can click the main form or the
    About button.

    What I'm trying to do is create a show/hide form (so it preserves state between appearances).
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mark Summerfield@mark@qtrac.eu to comp.lang.tcl on Fri Jun 21 15:17:56 2024
    From Newsgroup: comp.lang.tcl

    On Fri, 21 Jun 2024 09:55:13 +0000, Mark Summerfield wrote:

    Below is a tiny program.

    The "About" form is created and destroyed. So you can click About then
    close the About form, then repeat as often as you like.

    The "Options" form is supposed to be shown and hidden. But once you
    click it and then close it, the entire app is blocked. So clearly I'm
    doing something wrong.

    Here's the code using Tcl/Tk 9.0b2 on Linux.

    #!/usr/bin/env wish9

    if {[info exists env(TK_SCALING)]} { tk scaling $env(TK_SCALING) }

    proc prepare_modal {form} {
    wm withdraw $form wm attributes $form -type dialog wm transient
    $form .
    }

    proc show_modal {form {focus_widget ""}} {
    wm deiconify $form raise $form focus $form if {$focus_widget ne ""}
    {
    focus $focus_widget
    }
    catch {grab set $form}
    catch {tkwait visibility $form}
    }

    variable optionsForm

    proc main {} {
    wm title . "Dialog Tests"
    wm minsize . 240 240 wm attributes . -type dialog ttk::button
    .optionsButton -text Options… -underline 1 \
    -command on_options
    ttk::button .aboutButton -text About -underline 1 -command on_about
    pack .optionsButton -side top pack .aboutButton -side top
    }

    proc on_options {} {
    if {![info exists ::optionsForm]} {
    puts "on_options init"
    set ::optionsForm [toplevel .optionsForm] prepare_modal
    .optionsForm wm protocol .optionsForm WM_DELETE_WINDOW {wm
    withdraw .optionsForm}
    ttk::label .optionsForm.label -text Options pack
    .optionsForm.label
    }
    puts on_options ;# TODO use show_modal hide/show show_modal
    $::optionsForm ;# TODO give focus widget
    }

    proc on_about {} {
    toplevel .aboutForm prepare_modal .aboutForm wm protocol .aboutForm
    WM_DELETE_WINDOW {destroy .aboutForm} ttk::label .aboutForm.label
    -text About pack .aboutForm.label show_modal .aboutForm
    }

    main

    I think I have solved it this time.

    I added this proc:

    proc hide_modal {form} {
    wm withdraw $form
    grab release $form
    }

    and changed the protocol call to:

    wm protocol .optionsForm WM_DELETE_WINDOW {hide_modal .aboutForm}

    It seems to work, but is this now correct?
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Fri Jun 21 16:56:53 2024
    From Newsgroup: comp.lang.tcl

    Mark Summerfield <mark@qtrac.eu> wrote:
    Below is a tiny program.

    The "About" form is created and destroyed. So you can click About then
    close the About form, then repeat as often as you like.

    The "Options" form is supposed to be shown and hidden. But once you click
    it and then close it, the entire app is blocked. So clearly I'm doing something wrong.

    Based on your code you are showing the options as a "modal" dialog.
    Modal means it blocks the rest of the app. Withdrawing the window does
    not change its "modalness". You need to not only withdraw the window
    but also turn off the "modalness" of the window at the same time.

    I.e., you need to "grab release" on the window as part of withdrawing
    it.


    As a suggestion, you might also consider making the options window
    'non-modal' in that it can be open and the rest of the app can still
    function. Modality is more often evil than anything else in a GUI.


    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Rich@rich@example.invalid to comp.lang.tcl on Fri Jun 21 16:58:28 2024
    From Newsgroup: comp.lang.tcl

    Mark Summerfield <mark@qtrac.eu> wrote:
    On Fri, 21 Jun 2024 09:55:13 +0000, Mark Summerfield wrote:

    Below is a tiny program.

    The "Options" form is supposed to be shown and hidden. But once you
    click it and then close it, the entire app is blocked. So clearly I'm
    doing something wrong.


    I think I have solved it this time.

    I added this proc:

    proc hide_modal {form} {
    wm withdraw $form
    grab release $form
    }

    and changed the protocol call to:

    wm protocol .optionsForm WM_DELETE_WINDOW {hide_modal .aboutForm}

    It seems to work, but is this now correct?

    Yes, the 'grab' is what makes the options dialog modal. You also need
    to release the grab to remove the modalness so the rest of the app can
    receive UI events.
    --- Synchronet 3.20a-Linux NewsLink 1.114
  • From Mark Summerfield@mark@qtrac.eu to comp.lang.tcl on Sat Jun 22 09:44:12 2024
    From Newsgroup: comp.lang.tcl

    On Fri, 21 Jun 2024 16:56:53 -0000 (UTC), Rich wrote:

    Mark Summerfield <mark@qtrac.eu> wrote:
    Below is a tiny program.

    The "About" form is created and destroyed. So you can click About then
    close the About form, then repeat as often as you like.

    The "Options" form is supposed to be shown and hidden. But once you
    click it and then close it, the entire app is blocked. So clearly I'm
    doing something wrong.

    Based on your code you are showing the options as a "modal" dialog.
    Modal means it blocks the rest of the app. Withdrawing the window does
    not change its "modalness". You need to not only withdraw the window
    but also turn off the "modalness" of the window at the same time.

    I.e., you need to "grab release" on the window as part of withdrawing
    it.


    As a suggestion, you might also consider making the options window 'non-modal' in that it can be open and the rest of the app can still function. Modality is more often evil than anything else in a GUI.

    I was trying to create a family of functions to support creating dialogs
    of the kinds: modal and modeless hide/show (i.e., created once and
    reused), and modal create/destroy, and have now done so.

    As for modality being "evil". Over the decades many aspects of UI design
    have come and gone, mostly driven more by fashion than by issues of
    usability or functionality. Modality is confusing and inconvenient for
    some or even many users in some contexts, but it can be very convenient
    and useful for others in other contexts. The same is true of MDI (multiple document interface), a UI idea which has been out of fashion for at least
    25 years but which remains useful for some applications in some contexts.
    (I use it for a "pinboard" type application, and I've seen other current uses.) There are many other fashionable inconveniences I could mention.
    For example, some of my notification icons are black and white when color would be more useful.

    Personally I think a UI toolkit's role is to provide functionality and it
    is for the programmer to choose what to do with that functionality. I
    dislike toolkits which try to impose on the programmer (I'm thinking of
    Gtk here for example).
    --- Synchronet 3.20a-Linux NewsLink 1.114