/home/amgsk

Evinceの検索ハイライト色をGTKで上書きする

· amgsk

Evince で PDF を検索したとき、ヒットした箇所にハイライトが付く。 このハイライトの色は、デフォルトだと GTK テーマのアクセントカラーが使われる。自分の環境(adw-gtk3-dark + accent_color: #a8c8ff)だと薄い青になっていて、PDFビューアのハイライトとしては地味すぎる。

普通の PDFビューア風に「通常マッチは黄色、カーソル位置のカレントマッチは薄い赤」にしたい。 これを GTK の CSS でやる方法をメモしておく。Evince のソースを覗いて初めて分かった :selected:active の罠がポイント。

確認した環境

  • Arch Linux (kernel 7.0.12-zen1-1-zen)
  • evince 1:48.4-1
  • GTKテーマ: adw-gtk3-dark

既存のカラー定義を確認する

~/.config/gtk-3.0/gtk.css には、libadwaita系のセマンティックカラーを定義してある。

~/.config/gtk-3.0/gtk.css 抜粋
1
2
3
4
5
6
@define-color accent_color #a8c8ff;
@define-color accent_fg_color #05305f;
@define-color window_bg_color #121317;
@define-color window_fg_color #e3e2e7;
@define-color view_bg_color #121317;
@define-color view_fg_color #e3e2e7;

Evince の検索ハイライトもこの accent_color を継承しているので、何もしないと薄い青のハイライトになる。

EvView の検索ハイライト用 CSS を追記する

結論から書くと、以下の CSS を ~/.config/gtk-3.0/gtk.css に追記する。 セレクタの evview.view.content-view 部分(widget 階層と追加クラス)は、GTK_DEBUG=interactive evince で GtkInspector を起動し、ハイライト中の要素を実際にピックして確認した。 末尾の .find-results がポイントで、これは検索結果ハイライト専用に Evince が追加している CSS クラス(出所は後述)。

~/.config/gtk-3.0/gtk.css に追記
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/* Evince の EvView 検索ハイライト
 * 通常マッチ: 黄色 / カレントマッチ: 薄い赤
 */

evview.view.content-view.find-results:selected {
  background-color: rgba(255, 235, 59, 0.4);
  color: transparent;
  border-radius: 4px;
}

evview.view.content-view.find-results:active {
  background-color: rgba(255, 100, 100, 0.5);
  color: transparent;
  border-radius: 4px;
}

‘highlight-demo’

ポイントは3つ。

  • セレクタは .find-results:selected.find-results:active通常マッチとカレントマッチを別の擬似クラスで分けている
  • 背景色は rgba() で半透明にする。#fff59d のように不透明な色にすると、ハイライト下のPDFテキストが見えなくなる
  • color: transparent にして、CSS側で文字色を上書きしない。PDF元のテキスト色をそのまま透けて見せる

ハマったところ: :selected:active は絶対に成立しない

最初は「カレントマッチ=より強調された選択」と思って、こう書いていた。

ダメな例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* 通常マッチ */
evview.view.content-view:selected {
  background-color: yellow;
}

/* カレントマッチ(フォーカス中の選択) */
evview.view.content-view:selected:focus,
evview.view.content-view:selected:active {
  background-color: red;
}

これはカレントマッチには絶対に当たらない

Evince のソース (gnome-48 ブランチ ev-view.c:7461-7467) を見ると、検索結果の描画はこうなっている。 ここに登場する EV_STYLE_CLASS_FIND_RESULTS の定数値が "find-results" で、CSS 側からは .find-results クラスとして当たる仕組み(GTK の慣習で、C 側の定数名は大文字スネークケース/CSS 側はそれをケバブケースにした文字列リテラルで対応する)。

ev-view.c から引用
1
2
3
4
5
6
7
8
9
gtk_style_context_save (context);
gtk_style_context_add_class (context, EV_STYLE_CLASS_FIND_RESULTS);
if (active)
    gtk_style_context_set_state (context, GTK_STATE_FLAG_ACTIVE);
else
    gtk_style_context_set_state (context, GTK_STATE_FLAG_SELECTED);

gtk_render_background (context, cr, ...);
gtk_style_context_restore (context);

注目するポイントは gtk_style_context_set_state() を使っていること。これは state を置き換える動作なので、既存の state は全部消えて指定したフラグだけが立つ。

つまり実際の state の組み合わせは:

  • 通常マッチ → :selected だけ立つ(:active は立たない)
  • カレントマッチ → :active だけ立つ(:selected は立たない)

両者は同時に立たない排他関係。だから :selected:active のような複合セレクタは構造的に成立しなくて、:selected:focus も同じ理由でハズレ。

border-radius がカレントマッチだけ角ばる問題

最初は border-radius を書いていなかったけど、:selected だけ角が丸くなって :active のほうは四角いままだった。 これは GTK テーマ(adw-gtk3-dark)側で :selected 状態に border-radius を当てているケースが多いから。:active 単独のスタイルは定義されていないので、CSS のカスケードが効かずデフォルトの角ばった見た目になる。

両方に明示的に border-radius を当てて揃えるのが安全。

反映方法

GTK の CSS は通常、アプリ起動時にロードされるので、Evince を一度終了させてから開き直す。

1
2
pkill evince
evince /path/to/your.pdf

GtkInspector でセレクタを確認する

CSS が当たらない時や、別の widget をカスタマイズしたい時に、widget に付いているクラス名と state を実際に確認する方法。今回のセレクタ evview.view.content-view.find-results もこのやり方で裏付けた。

GTK_DEBUG=interactive 環境変数を付けて Evince を起動すると、本体ウィンドウと一緒に GtkInspector のウィンドウが開く。

1
GTK_DEBUG=interactive evince your-file.pdf

手順:

  1. Inspector の左上にある「Pick a widget」ボタン(照準アイコン)をクリック
  2. Evince 側で調べたい要素(例: 検索ハイライト中の箇所)をクリック
  3. Inspector の「Objects」タブで対応する widget ノードがハイライトされる
  4. 右側の「CSS Nodes」タブを開くと、ノードの階層と各ノードに付いているクラス名・state がツリー表示される

検索ハイライトを pick した場合、EvView の配下に evview.view.content-view ノードが見えて、検索状態のときだけそこに .find-results クラスと :selected / :active の state が乗ってくる。これが今回のセレクタの根拠。

「CSS Nodes」タブには Style ClassesState の編集UIもあるので、その場でチェックを付け外しすればCSSがどう当たるかその場で確認できる。新規セレクタを試す時にいちいち Evince を再起動するより早い。

ハイライトの矩形そのものをピックしようとすると EvView 全体に当たって .find-results が見えないことがあるので、その場合は「Objects」タブで EvView ノードを選択し、子要素を展開して find-results 付きのノードを探すのが確実。

色味の調整目安

rgba() の値はお好みで。参考までに、自分が試した範囲では:

  • 通常マッチ
    • もっと薄く(テキスト視認性優先): rgba(255, 235, 59, 0.25)
    • 蛍光ペン風(暖色寄り): rgba(255, 245, 157, 0.5)
  • カレントマッチ
    • もっと淡く(パステル寄り): rgba(255, 138, 138, 0.4)
    • ピンク寄り: rgba(255, 150, 180, 0.45)

ハマったところまとめ

  • Evince の find-result は set_state で state を置き換えするので、:selected:active は排他。:selected:active などの複合セレクタは絶対に当たらない
  • background-color は不透明色にするとPDFテキストが下に隠れる。rgba() で半透明にして color: transparent を併用する
  • border-radius:active 単独だとテーマから継承されないので、両方に明示的に書く
  • セレクタが当たらない時は GtkInspector で実際のクラス名と state を直接確認するのが早い(手順は前述)

EOF