2015-02-11

QtでWindows95スタイルのツリーコントロールを再現する

各OSごとの見た目の違い

OSの見た目は、バージョンが上がる毎に変化していきます。その変化が改良なら良いのですが、しばしば「改悪」が行われます。WindowsとMac OS XとLinuxで、見た目が異なるのはもちろんのこと、Windowsでも95と7では違いますし、Linuxでもフレームワークやウィンドウマネージャが違うと、見た目が違います。

まあ、違うのは仕方ありません。問題は、以前良かったものが、新しくなって、わかりにくく、使いにくくなることがあります。

下の画像はWindows 3.1のファイルマネージャのツリーコントロールです。

当時のコンピュータの性能からして仕方ないのですが、とてもシンプルで無駄な飾りの無い描画です。

次はWindows 95のエクスプローラです。

ツリーの開閉できるポイントが、プラスとマイナスのボタンになっていて使いやすく、階層構造も見やすくなっています。

そして、Windows Vistaでも変わりました。

開閉ボタンが三角アイコンになりました。

さらに、Windows 7ではこうなります。

線が消えてしまいました。

三角形アイコンより、プラスマイナスボタンの方が、開閉できることが一目で直感的にわかりやすく、また、枝の線があった方が、ツリーの階層を把握する助けになります。以上のことから、ツリーコントロールの変貌を見ると、Windows 95が最高で、それ以降、劣化していく一方であることがわかります。

次は、OS X 10.9 (Mavericks) です。

開いている枝と閉じている枝の三角形の向きが違うだけで、色も同じで見分けにくいですね。OS Xがいかにユーザーにとっての使いやすさを無視して設計されているかが如実に表れています。

どのOSでも同じ見た目を実現する

基本的に、OSが変われば見た目が変わるのは当然で、郷に入れば郷に従えというように、それぞれの文化を尊重するのが、基本姿勢としてはそうあるべきでしょう。しかしこのツリーコントロールに関しては我慢ができません。

せめてQtを使って開発するアプリケーションだけでも、各プラットフォーム共通で、Windows 95スタイルのツリーコントロールを再現したいと思います。

LegacyWindowsStyleTreeControl.zip

Windows 7 で実行すると以下のようになります。

OS X で実行すると以下のようになります。

使い方

QProxyStyleを継承したクラスを作成します。LegacyWindowsStyleTreeControlオブジェクトをメンバとして持ちます。

class MyStyle : public QProxyStyle {
private:
  LegacyWindowsStyleTreeControl legacy_windows_;
public:
  MyStyle()
    : QProxyStyle(0)
  {
  }
  void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = 0) const
  {
    if (element == QStyle::PE_IndicatorBranch) {
      if (legacy_windows_.drawPrimitive(element, option, painter, widget)) {
        return;
      }
    }
    QProxyStyle::drawPrimitive(element, option, painter, widget);
  }
};

drawPrimitive関数をオーバーライドして、element == QStyle::PE_IndicatorBranch のときだけ、LegacyWindowsStyleTreeControlのdrawPrimitive関数に委譲します。

新しく作ったスタイルオブジェクトをQApplicationに登録します。

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  QStyle *style = new MyStyle();
  QApplication::setStyle(style);
  MainWindow w;
  w.show();
  return a.exec();
}

これだけで、基本的には動作しますが、アイテムの高さは4の倍数にすることをおすすめします。4の倍数でない場合は、描画がずれて見た目が悪くなることがあります。

QTreeWidgetItemオブジェクトを作成する際、常にsetSizeHintを実行すると間違いがありません。

QTreeWidgetItem *new_QTreeWidgetItem(QTreeWidgetItem *parent)
{
  QTreeWidgetItem *item = new QTreeWidgetItem(parent);
  item->setSizeHint(0, QSize(20, 20));
  return item;
}