Text versus TextPainter

This is the story of an issue I discovered while using Flutter, a cross-platform framework for building apps that can run on iOS, Android, macOS, Windows, and the web. (As an aside: it’s pretty good! I have been using it to implement cross-platform iOS and Android apps for a few years now. I don’t have any major complaints.)

The most prominent way to add strings of text to a Flutter app is by using the Text widget. This is the way you want to go, about 95 percent of the time. But there are occasionally situations in which a Text widget will not get the job done. For example, say you are building a graphical image by drawing pixels on top of a Canvas object, and you also want to add some text in there. In that case, TextPainter is the right tool for the job. It operates in much the same way as Text does, but it is intended for drawing on a Canvas, rather than being added to a widget tree, which is what Text objects are for.

Flutter has adopted the React widget model, as opposed to the “old-fashioned” declarative paradigm, used by many other GUI frameworks. I put “old-fashioned” in scare quotes there, because this is a decision that I very much disagree with. One of my few complaints about Flutter is the React widget model. This causes me no end of grief. But there is no major piece of tech infrastructure of this scale that is going to incorporate nothing but decisions I agree with. So I have made my peace with the React widget model. (Grudgingly. A lot of the Flutter code I write is to put a nice, declarative layer on top, implemented with Flutter's React-style widgets under the hood. But I digress.)

more …

new project

Harold and Angelyne

Today I am going live with the first version of my fan site for the Angelyne miniseries.

Holy crap, who am I? I have gotten pretty involved in this or that teevee show or film over the years, but going to this much effort, for just one of them? I don’t recognize myself.

Last night, I spent four or five hours transcribing the first episode of the show. I was only able to write up about twelve minutes of it. Unbelievable. At this rate, it might well take me a year or more to finish the whole series.

In the process, I was forced to pay a lot more attention to the fine minutiae of the show than I had before. I am dismayed to discover that it resorts to a lot more tired clichés and well-worn tropes than I had noticed on my first viewing. I suppose I was too dazzled by the subject matter to notice.

Emmy Rossum’s take on Angelyne is such a surprise to me. I have spent decades absorbing material about this person. My opinion of her is not great. But Emmy found a way to interpret all those stories I was already familiar with in the best possible light, in a way that makes Angelyne seem like she has well-defined goals and a purpose, without feeling fake or forced. Emmy’s optimism and big-hearted interpretation of the material really worked for me. It helps paper over a lot of the criticisms I would have normally leveled at a show like this.

My attempts at transcription helped me appreciate how much work goes into a show like this. For every second of footage onscreen, an unbelievable number of decisions are being made by the creatives, over and over again. I am going to casually estimate that, for every hour-long episode of teevee you watch, roughly ten thousand hours of labor were required to bring it to life. Think about that for a minute. You watch an episode of your favorite teevee show, it’s over before you know it, and you barely remember it a month later. But for dozens or hundreds of people who worked on it, it was all they thought about, for months-long stretches of time. Makes you want to be a little more sympathetic, doesn’t it.

protocols, not platforms

Does everybody get to coin one maxim in their lives? If so, here’s mine: Everything gets worse.

Let’s say you’re shopping at the grocery, when you notice a new food product. It competes directly with another product you’ve been buying, which you are not super happy with. You like the looks of this one, so you buy it. You get it home, and what do you do know, it’s great! It might cost a little more than the alternative you’re used to buying, but you’re willing to pay for quality, right? I know I am.

Weeks and months go by, as you continue consuming your newly-discovered food, still happy to pay more than the inferior competitor. You are not the only person who loves this new product, and some genius at the company takes notice. Holy cow, this thing is selling like hotcakes! Surely we can make even more money. Cut corners just a tiny bit. Put a half-ounce less of the product in the container every now and then. Use cheaper ingredients. Extend the expiration dates, so older product won’t be discarded unsold. They’ll keep buying it, due to the great reputation we’ve built up!

Sooner or later, this great new product that you loved has reverted to the mean, and is maybe even worse than the thing it replaced. Back to the drawing board.

more …

apple music: stop after this track

Stop After This Track AppleScript

Say you’re listening to Apple’s Music app on your Mac. You know you want to quit pretty soon, but you want the break to come at the end of the track you are listening to, rather than abruptly quitting at this very instant. (What a vulgar concept.) So you start up this AppleScript I wrote. It keeps tabs on the track that is currently playing. When the Music app switches to the next track, the script will tell Music to stop playing.

(Note that the app has a “Stop” button. Pressing that button will not stop the Music app playing prematurely, as you might expect, but instead exits the AppleScript altogether, without it doing anything further.)

Yep, that’s its sole purpose. Does that sound trivial and pointless to you? I often wish I was that type of person myself. If you feel that way, feel free to move right along to any of the other near-infinite reading possibilities that the internet has to offer you!

more …

async/await cancellation

Flutter Async/Await Cancellation Demo App

The Problem

I spent about a decade working as an iOS programmer. On that platform, in that timeframe, if you wanted to write an operation that would take so long that it would disrupt the user interface, you needed to wire up callback functions for every eventuality. The most common example is a network request. You would typically need a callback that will be called if the request succeeds, and another one if it fails.

But now here we are in the bright shiny future. These days, callbacks are, like, so last-century. Who wants to write three functions — the network request itself, and its two mandatory callbacks — when you can instead write only one? Such is the promise of async/await, the brand-new hotness.

I could waste a lot of time and effort writing about whether we’ve gained anything with this move or not. (Spoiler alert: my own personal belief is that async/await is one step forward, two steps back.) But there is no point in bloviating on this topic any further. That ship has sailed. My side lost. (For the moment. These things have a way of going in cycles.) It’s better to just get on with it, and make peace with the status quo.

more …

flutter finger-signing

Flutter Finger-Signer Demo App

One of the first tasks I was given, as a professional Flutter developer, was to implement a widget that would allow users to create a signature by drawing on the surface of a mobile phone. So I did what most people do: I searched the internet to see how other programmers had dealt with this problem.

What I found was several finger-signing widgets, differing in most of the details, but all implemented pretty much exactly the same way. They all maintained the widget’s state as a list of Offset objects. (Flutter uses an Offset to represent what you would call a point in most other graphical environments.)

I tried the first one. Doing a test-signing, using the mouse on the surface of the iOS emulator, it quickly became unusably sluggish. You’d be surprised how quickly your finger-scribblings can add up to hundreds or thousands of saved points. And given that Flutter forces you to throw away all saved state and recreate it on every redraw, it was quickly adding up to thousands of line-draws per second.

Android has much worse graphical performance than iOS, so it was not surprising that the widget performed even worse on the Android simulator. On my mid-range Motorola Android phone that I used for testing, it was so bad that it routinely crashed after a mere second or two of screen-scribbling.

The other two or three finger-signing widgets I tested all suffered from the same issues: sluggish updates, close to unusable on iOS devices, crashing within seconds on Android devices.

more …

home, home on the web

On the occasion of implementing yet another iteration of my personal website, I thought this would be a good time to reflect on all my various web dalliances over the years.

In the Beginning

I created my very first personal website in roughly 1996. I was living in Miami at the time, and I had a dial-up account with Concentric. (Pretty sure that company was absorbed into some larger ISP about a million years ago.) This was before it was a “thing” for civilians to host on their own domains, so my site address was something like http://www.concentric.net/~username.

It was all poorly hand-coded HTML, which looked pretty shabby. My only saving grace was that everybody else was also making ugly personal websites, so mine didn’t seem all that out-of-place.

The end of my Concentric website came about in an unexpected way, in the year 2000. I was building a new page for my as-yet-unreleased BeOS newsreader, Pineapple News, but I was having some kind of technical problem. I posted a link to the new page in one of the BeOS-oriented USENET newsgroups, along with a technical question. Some kind soul helped me solve the problem I was having (pretty sure it involved editing the apache .htaccess file), but I also got a lot more than I bargained for.

The BeOS community saw what I was working on and went absolutely nuts. I got so many site visits in the next 12 hours that Concentric took my personal site offline. I got an email implying that they were going to terminate my account, if I did not find a way to curb the onslaught of traffic.

more …

mac backup script

Most backup systems assume you are lazy and inattentive, and if they don’t do pretty much everything for you, it won’t get done. So let’s say you dutifully set up your backup software as directed, “set and forget.” It starts humming along, doing backups every so often on its own schedule.

Fast forward to months or years later. Your primary copy of your data has been ripped away from you, due to misadventure, fire, theft, or equipment failure, and you want to get it back. Surprise! Your backups stopped working shortly after you enabled them, for whatever reason: the backup disk failed or is no longer connected to your computer, the disk filled up, or the software is misconfigured. The most recent “backup” you have is many months out of date.

Even if I could 100 percent trust that “automatic” backups would never fail, I still wouldn’t want to do it that way. If you’re a programmer, you would never set up git so that it automatically commits everything you’ve changed every hour or so, would you? Me neither. I feel the same way about my backup software. I want backups to occur only when my files are in a known-good, consistent state.

more …