Files
bernardo 9a9d9f3fea Move flags/timer out of the header bar into a status bar above the grid
Header bar now holds only Difficulty + Leaderboard. A status bar above the
board shows the flag counter (left), the New Game button (center) and the
timer (right) as LCD-style panels styled with theme-aware CSS.
2026-08-01 16:30:46 +01:00

5.2 KiB

AGENTS.md

GTK4/gtkmm4 Minesweeper clone ("NoMines") in C++20, built with Meson. ~800 lines total.

Build & run

meson setup build        # or reuse existing build/
meson compile -C build
./build/nomines
  • Dev deps come from the Nix flake via direnv (.envrc uses use flake); nix develop works standalone.
  • Alternative: nix build / nix run.
  • No unit tests; two optional release-validation tests (desktop file + AppStream metainfo via desktop-file-validate/appstreamcli) are auto-added when those tools exist.
  • build/compile_commands.json exists for clangd (it's gitignored, regenerate with meson setup --wipe build if stale).
  • WARNING: flake builds only see git-tracked files. New source files must be git added before nix build works.

Architecture

  • src/minefield.* — pure game logic, no GTK: Minefield (board state, flag counting, chording), Cell bitfield, GameDifficulty presets, GameState enum.
  • src/board_widget.*Gtk::DrawingArea; all rendering is hand-drawn Cairo primitives (draw_cell/draw_digit/draw_flag/draw_bomb), input via Gtk::GestureClick + EventControllerMotion, confetti particle animation on win (frame-rate independent via Gdk::FrameClock).
  • src/window.*Gtk::ApplicationWindow with HeaderBar (difficulty + leaderboard popovers, radio-style CheckButtons) and a status bar above the grid (LCD-style flags/timer panels via the status-panel CSS class, New Game button centered); owns the shared_ptr<Minefield> and swaps it on new game. Keyboard shortcuts via Gtk::EventControllerKey (R/Ctrl+N new game, 1-4 difficulties, L leaderboard). App-wide CSS (popover backgrounds, status bar) lives in a Gtk::CssProvider in the constructor.
  • src/leaderboard.* — persistent best times per difficulty via Glib::KeyFile at ~/.config/nomines/leaderboard.ini (recorded on win, shown in a header-bar popover; falls back to the pre-1.0 ~/.config/minesweeper/ path for migration). NOTE: uses the glibmm 2.80 refcounted KeyFile::create() API and gtkmm 4.14 CheckButton (a Widget+Actionable, not a Button — use signal_toggled() + get_active(), and set_group(CheckButton&)).
  • glibmm 2.80 quirk: SignalProxy<R(T...)>::connect() (non-void return signals like signal_key_pressed) requires the after bool explicitly — connect(slot, false). Void signals default it to true.
  • src/main.cpp — single Gtk::Application (io.github.bemagri.nomines); make_window_and_run<MainWindow>.

Release

  • Version 1.0.0 is stamped in FOUR places: meson.build, flake.nix, default.nix, and the <releases> entry in resources/io.github.bemagri.nomines.metainfo.xml. Bump all four together.
  • Release sanity: nix build . (validates the flake), desktop-file-validate + appstreamcli validate --no-net on the resources (wired up as meson tests).
  • The metainfo screenshot URL points at raw.githubusercontent.com/bemagri/nomines/main/... — it 404s until the repo is published; replace if the repo moves.
  • Flatpak: io.github.bemagri.nomines.yml builds libsigc++/glibmm/cairomm/pangomm/gtkmm from tarballs against the GNOME 46 runtime (matches the gtkmm 4.14/glib 2.80 stack we develop against), then the app from the v1.0.0 git tag. The pinned commit: must match the pushed tag. The leaderboard needs the --filesystem=xdg-config/nomines:create finish-arg — don't drop it. Local builds land in .flatpak-builder/ (gitignored); flatpak-builder --user --install --force-clean build-flatpak io.github.bemagri.nomines.yml.
  • Flatpak module version traps (all verified by building): gtkmm 4.14 needs pangomm >= 2.50 (which still installs pangomm-2.48.pc; 2.48.x is too old), and cairomm tarballs live on cairographics.org/releases, not download.gnome.org. appstream-compose: false is set in the manifest because flatpak-builder's compose fetches the (still-404) metainfo screenshot URL — remove it once the repo is public. Flatpak's icon validation can't decode SVGs, so PNG icons (128/512) are installed under the app-id name and the svg is cleanup-removed; keep that convention.

Gotchas

  • resources/gresource.xml + the gnome.compile_resources step in meson.build compile unused code: the app never loads these SVGs/textures (the confetti.png is also unused). All art is Cairo-drawn. Don't extend the GResource to implement visual features; draw with Cairo instead.
  • README's feature list is mostly accurate but not trustworthy in detail; verify against code. (An older leaderboard.txt CSV may linger in ~/.config/minesweeper/ from a previous experiment — the app now uses ~/.config/nomines/leaderboard.ini.)
  • Code style: snake_case methods/fields, trailing _ on private members, #pragma once, nested-struct headers. C++20 stdlib only, no third-party libs beyond gtkmm/sigc++.
  • Board colors are theme-aware: BoardWidget::resolve_palette() maps GTK CSS named colors (theme_bg_color, theme_base_color, ...) to a light/dark palette each draw. Don't hardcode light grays in draw_* helpers; thread the Palette through instead.
  • Meson source list is explicit in meson.build — new source files must be added there.
  • .direnv/ and build*/ are gitignored; flake builds require files to be committed/tracked.