Code by Kevin, Programming, code, business, and other pursuits
Kevin Walzer, software developer.
Subscribe to RSS Feed
Get a syndicated feed of my weblog.
Site design: Skeleton
Since I'm celebrating adding Cocoa to my toolkit to improve my Tcl/Tk applications on the Mac, I thought I'd share a bit about the strategies I use to make my Tk applications as Mac-native as possible. As a cross-platform toolkit, Tk doesn't have all the OS-specific hooks that an application built in Cocoa does--but it isn't too hard to give it many of those hooks.
Here are some questions I ask myself in trying to make my Tk applications Mac-native:
1. Is the native functionality built-in to Tk? Before I think about extending Tk in any way, I look for functions where Tk wraps native functionality. It does so in a surprising number of ways. For instance, Tk supports sheet-style dialogs on the Mac, which is a nice touch; I use these wherever possible. Since Tcl/Tk 8.5, I can also bounce my Tk applications on the Dock with a simple command. Also, Mac-specific window flags are also there, for things like utility/floating windows.
2. Is the functionality available on OS X from a command-line interface? Growl notifications can be called via AppleScript and osascript; no Tcl binding/wrapper needed, although one was in development at one point (and is no longer supported). Text and graphics printing can be called from the classic Unix "lpr" command; I use my own dialog box to provide visual feedback on this. It's not as native as using Cocoa printing and dialog boxes, but is adequate for what my applications require. Finally, when my programs need to execute commands with elevated privileges, they can call these commands via the classic Unix "sudo" command. (As I've written before, I use the Mac's Unix underpinnings in the same way that Cocoa developers use Cocoa functionality--as a powerful development framework.)
3. If a native solution cannot be easily obtained, can the functionality be emulated/imitated from Tcl? Sparkle is a popular Cocoa-based application update mechanism. It lacks Tcl bindings, but I found it simple enough to develop my own Tcl package that implements similar functionality. I have also developed open source Tcl/Tk packages that implement a Mac-style toolbar, Mac-style tree and table views, a Mac-standard "window" menu, and more. If I am able to develop a Tcl script-level package that implements Mac-style functionality or look-and-feel in a way that does not seriously compromise my users' expectations, I will do that. (As well, I have developed--and discarded--some packages that attempt to implement Mac-style functionality, but fall too short of what Mac users expect.) I also use third-party script libraries like TclMacBag to implement Mac-style look-and-feel.
4. Add compiled binary packages written in C. These can be easy to highly complex. Even at their simplest, they add overhead to building/deploying my applications. At the more complex end, they add a huge amount of complication. The dividends had be better worth it. In the case of tkdock, I felt that developing this compiled binary package was worth the time and effort. I found another library, TkCximage, in the aMSN instant messaging program; TkCximage is a powerful image library that allows image resizing. I needed this functionality for use in my help viwer and in other parts of my applications and could not find such functionality elsewhere, so I added this package to my development toolbox.
I hope this gives you some idea of the time and effort I expend in trying to provide the best possible user experience for my programs using a cross-platform toolkit. I have invested years of work in learning Tcl/Tk; I am not eager to simply abandon that work, which has allowed me to ship five commercial Mac programs, with more under development. However, I understand the value of using Mac-natve languages and frameworks, specificially Cocoa, to extend Tcl/Tk where necessary, and I also appreciate the way the Tcl/Tk developers have already wrapped a good deal of Mac-native functionality in the language and toolkit.
Yes, you just heard the sound of hell freezing over. Yes, I've sworn up and down how much I hate Cocoa and would never learn it.
Well. Why the change of heart?
First, I'd say that it's only a partial change of heart. I'm still a Tcl/Tk developer first and foremost. I have no interest in becoming a full-blown Cocoa developer, using Objective-C as my development language and Xcode/Interface Builder as my development environment.
However: there are things I can't do in pure Tcl/Tk. Things like changing the application's Dock icon while it's running. If I want to do that, I have to drop down into a different, lower-level programming language like C or Objective-C. I know I've complained about this before as well. However, after coming back to Objective-C again over the past couple of months and working through Stephen Kochan's book on Objective-C, something clicked. I finally was able to understand how compiled code worked, and how to organize code in C/Objective-C--in header files, source files, and so on. After some additional reading in Brent Welch's superb textbook on Tcl/Tk programming, I also understood how to extend Tcl at the C level.
In other words, I finally felt comfortable trying to tackle a small Tcl extension using Cocoa (since that is now Apple's preferred development framework; Apple's Carbon frameworks are now deprecated).
It didn't go flawlessly. I wasn't sure where to start at first, because while I felt comfortable with Objective-C and Tcl's C extension API, there was almost no sample code out there on how to combine them. (I finally found Tcl bindings for the Growl notification system; these were written in Objective-C and showed me how to call into Cocoa methods from Tcl C code.) And after I finally got started, I ran into several roadblocks with things not working as expected, with memory leaks, and with random crashes--all the things that make developing in compiled languages more complicated and slower than developing in scripting languages.
I continued, however, and with some guidance from Daniel Steffen, the maintainer of Tcl/Tk on the Mac, I finally began to make progress. And then today, everything worked without a hitch: tkdock was ready to go!
That's a feeling of real accomplishment. For the first time, I have built my own program in C-level code, learning as I went, understanding what I learned, and using my own code--not blindly copying and tweaking someone else's code. Some things simply can't be done at the scripting language level, and I feel proud that I'm now able to drop into a lower-level language to get the functionality I want.
I can't say that I will replace scripting languages with Cocoa/Objective-C: my original sense that developing in Objective-C is slower and more complicated than Tcl or Python hasn't changed. But there is real power in C and its Objective-C offshoot: I can do things there that I simply can't do in Tcl (unless I implement Tcl-level functionality in C/Objective-C, as I did with tkdock). So Objective-C/Cocoa now has a permanent place in my toolbox.
I'm happy to report the release of tkdock 1.0, a small open-source Tcl extension that allows a Tcl/Tk application to change its Dock icon while running. This is a cool effect: it allows an application to indicate that it's in the middle of a long-running process, connecting with a server, or something similar. This package provides a nice visual touch that I plan to incorporate into most, if not all, of my applications. It can be downloaded from http://www.codebykevin.com/opensource/oss.html. There are other Tk extensions that provide similar functionality, but to my knowledge there are none that are developed as a single, simple extension package intended for general use in Tcl/Tk applications. As such, tkdock fills a void. Thanks to Daniel Steffen for his guidance.