Updated 2013-11-29 16:17:08 by pooryorick

Moved from Tk 9.0 WishList because it was getting a bit long to remain there.

See Also  edit

URL behaviour in a text widget

Description  edit

nagu - Hyperlink basic widget that includes all the functionality of <a> tag in HTML. I am not talking about the hyperlink tags inside text/canvas widgets. I should be able to create a hyperlink like creating a label widget.

RLE (2012-08-10): You mean something like this?:
$ wish
% font create Underline-Font {*}[ font actual TkDefaultFont ]              
Underline-Font
% font configure Underline-Font -underline true -size 12                   
% label .hyperlink -text "Click Me" -foreground blue -font Underline-Font
.hyperlink
% bind .hyperlink <Button-1> [ list puts "Click Me was clicked" ]
% pack .hyperlink
% # now click with mouse button one on the "hyperlink"
% Click Me was clicked

Creating a wrapper/snit-widget is left as an exercise.

nagu (2012-08-12): Yes.. thats what I meant. Thanks for the code snippet. However, it would be really nice to have a pre-built wrapped up hyperlink widget. That gives "standardized" interface to a widget that is most often needed and asked for in many discussions online. It deserves it and its a low hanging fruit to implement - IMHO.

RLE (2012-08-11): Given the trivial amount of effort needed to create this widget, creating a basic wrapper proc, or creating a snit widget that encapsulates this concept would appear to be relatively trivial.

I.e.: (very basic wrapper proc, this requires at least Tcl 8.5):
proc hyperlink { name args } {
  if { "Underline-Font" ni [ font names ] } {
    font create Underline-Font {*}[ font actual TkDefaultFont ]
    # adjust size below to your preference
    font configure Underline-Font -underline true -size 12
  }
  if { [ dict exists $args -command ] } {
    set command [ dict get $args -command ]
    dict unset args -command
  }
  # note - this forcibly overrides foreground and font options
  label $name {*}$args -foreground blue -font Underline-Font
  if { [ info exists command ] } {
    bind $name <Button-1> $command
  }
  return $name
}

And now you can do:
hyperlink .hl -command [ list puts "clicked" ] -text "Click Me"
pack .hl

To get a hyperlink widget created and mapped on screen.

nagu (2012-08-13 IST): I will also add -image option in addition to the text (underlined). Thanks again for the code snippet. We've already started implementing it. This proves the point that its a low-hanging-fruit to implement. Couple of additional points why we should pre-wrap it:

  • Helps avoid redundant implementation in every Tk application - point behind any wrapper anyway.
  • Makes life simple. I, as a not-so-technical programmer, try to avoid using bind and maintaining color of text when its clicked etc. etc.
  • Helps modernizing. That is, it helps to move away from old fashioned buttons (which people call it clunky!) to clickable text/image links.

RLE (2012-08-12): label already supports an -image option, so there is nothing to add. The only thing the wrapper really does is capture the "-command" option to create a binding, then pass everything else through to the underlying label, except that it does override the -font and -foreground options.

Using a tiny bit more code to add a missing -font and -foreground option into the $args dict you also get external control of -font and -foreground but get the defaults otherwise. I.e. (this also changes the cursor to the traditional URL hand when it is above the label widget):
proc hyperlink { name args } {

  if { "Underline-Font" ni [ font names ] } {
    font create Underline-Font {*}[ font actual TkDefaultFont ]
    # adjust size below to your preference
    font configure Underline-Font -underline true -size 12
  }

  if { [ dict exists $args -command ] } {
    set command [ dict get $args -command ]
    dict unset args -command
  }

  # add -foreground, -font, and -cursor, but only if they are missing
  set args [ dict merge [ dict create -foreground blue -font Underline-Font -cursor hand2 ] $args ]

  label $name {*}$args

  if { [ info exists command ] } {
    bind $name <Button-1> $command
  }

  return $name
}

nagu (2012-08-13 IST): Also, instead of being just a clickable text/image, it can be made a real hyperlink that accepts a -href option to take-in a URL and invokes a callback command to process the content fetched. The fetchers should be plug-ins based on the type of content (like http, file etc.). This feature will make it a real hyperlink widget.

RLE (2012-08-12): No need. Simply install a -command like this:
 -command [ list url-fetcher http://www.example.com/page/to/fetch ]

Then your "url-fetcher" proc can do whatever it wants to do with the url.

nagu (2012-08-13 IST): Thanks. Its getting better. By fetchers being plug-ins, I meant they are pre-wrapped too. Like image command having handlers pre-built for various types of images with extensible design, it will be good if we hide the details of fetching from users and Tklib or other libraries providing extended handlers for different protocols.

RLE (2012-08-12): Yes, but then you would no longer have a generic hyperlink widget. You'd have a specialized widget for a particular purpose, reducing the usefulness of the widget in general. I.e., assume that "fetchers" were built in, and clicking the hyperlink would "fetch" something. Now what? What does the widget do with the something that was fetched? The answer to that is "it depends on what the application needs to do with the thing fetched". Which means what to do when a click occurs should be the subject of a different module, not the hyperlink widget itself. The hyperlink widget should simply be a generic way to put a clickable link into a GUI. What the application does when the link is clicked is the responsibility of the command that the hyperlink widget calls to report that a click has occurred. In this way the generic hyperlink widget is useful for any of:

  • fetching a file
  • uploading a file
  • exiting the application
  • saving current work

when clicked. Or in other words it is useful for whatever the author of the application wants the click to represent.

nagu (2012-08-15 IST): I realize that I've clubbed both generic and specific requirements into one topic. I agree with your point that the label widget with -text, -image and mouse click callback processing should make the generic HyperLabel part. I think the improvements needed on this part is being discussed in request-no-89. Hence, I would like to leave it at this and focus on HyperLink: Yes lets call HyperLink a HyperLink. The requirements would be:

  • -text and -image options
  • default mouse enter & leave events processing to change text color and cursor
  • 'url' argument
  • pre-wrapped fetchers for well-known frequently used protocols (e.g., http://, file:/// etc.)
  • extensible design to add support for more content-types & protocols
  • -command option to specify the callback to be invoked upon a successful fetching of the content. If not configured, fetching will not happen.

with approximate syntax of:
hyperlink pathName ?-url url? ?-text text? ?-image image? ?-command cmd?

Now, I understand your question will be whether this is a basic widget or a mega-widget. I would argue that this deserves to be a basic widget based on the number of <a href> tags you will find in every html page in this world.

RLE (2012-08-15): I would not call your proposal a mega-widget, but rather an overloaded widget. You are proposing to overload the widget with lots of features/functions that are wholly unrelated to being a widget. If you look at all the other basic widgets in Tk (button, entry, checkbutton, menu, etc.) they are responsible for two aspects, 1) display, and 2) reacting to user input by signaling the rest of the script that some defined user interaction has occurred. It is then up to the rest of the script to perform whatever function is to happen as a result of the defined user interaction. I.e., a "button" widget is not configured to perform a "save" (or at least should not be, it is possible to do so if one wants to do so), it is configured to make a callback to a procedure the programmer writes. Then it is that procedure that performs the "save" (or whatever) function. That way there is a clean separation of concerns. The button widget handles display, animation, and signaling that a press has occurred. The called procedure handles the actual desired result from the button press.

A hyperlink widget, in keeping with that clean separation, should therefore be responsible for display of the hyperlink, any necessary animation that might occur, and simply signaling that a user clicked the link. It is then up to the procedure signaled to perform the desired function. Fetching content (be it http: or ftp:) is but merely one possible desired function of a hyperlink widget. Clean separation of concerns.

Feel perfectly free to create your own overloaded hyperlink widget with integrated fetchers and extensible content handling subsystem if that is what you want to create. After some more years programming, you will realize you don't actually want to mix all that together into one thing, because the simple generic version will prove to be far more reusable than the overloaded, special purpose version.

DKF (2012-08-15): Other reasonable options for how a hyperlink might be activated might be to copy it to the clipboard, or to dispatch it to the user's configured browser. Which is correct will depend on the application. Tk handles this sort of thing by allowing the widget instance to be configured with a callback to handle the high-level event (“hyperlink was clicked”) and take the correct action for the particular situation.

EE (2012-08-16): I don't understand how this is different from a simple button widget configured with underlined blue text.

RLE (2012-08-16): Other than being a label instead of a button widget configured with underlined blue text, it is not any different. It is just a small wrapper proc that saves one from having to remember to always configure the text, color, and binding. The purpose was to demonstrate to nagu that his request on the Tcl 9.0 WishList page was not a Tcl 9.0 wish at all, but was something that could be done now, with only a minimal amount of code to do so.

nagu (2012-08-28 IST): Thanks RLE for all your detailed responses. Those are all educative. I also learnt where to draw the line when it comes to a "UI Widget". Your wrapper code is useful. I am sure every Tk application would want to have one like that. If not Tk, I hope at least some widget library would want to include it (however small the work involved is) so that its not repeated by every Tk programmer.