// lib/editor/canvas/note_background.dart // // rnote-style page background TEMPLATES for the blank-note editor. A background // is a repeating PATTERN painted in the note page's local pixel space (the // `pageWidget` is sized to the page rect inside the InteractiveViewer, so a // CustomPainter here scales 1:1 with zoom — no extra transform needed). // // The choice is per-notebook and persists in the sidecar (stored as the enum // `name`; missing/unknown → [NoteBackground.blank] for back-compat). import 'package:flutter/material.dart'; /// The available page-background templates (rnote: blank + dots/lines/grid + /// the Cornell note layout). enum NoteBackground { /// Plain white sheet, no pattern. blank, /// A regular grid of small dots (dotted paper). dots, /// Evenly spaced horizontal lines (ruled / lined paper). ruled, /// Square grid (graph paper). grid, /// Cornell layout: a left cue-column line + a bottom summary line over a /// ruled note-taking body. cornell, } /// Decode a persisted background name (the enum [NoteBackground.name]); unknown /// or missing values fall back to [NoteBackground.blank] (back-compat). NoteBackground noteBackgroundFromName(String? name) { for (final b in NoteBackground.values) { if (b.name == name) return b; } return NoteBackground.blank; } /// Localized-ish English display label for the picker menu. String noteBackgroundLabel(NoteBackground b) { switch (b) { case NoteBackground.blank: return 'Blank'; case NoteBackground.dots: return 'Dots'; case NoteBackground.ruled: return 'Ruled lines'; case NoteBackground.grid: return 'Grid'; case NoteBackground.cornell: return 'Cornell'; } } /// An icon for the picker menu. IconData noteBackgroundIcon(NoteBackground b) { switch (b) { case NoteBackground.blank: return Icons.crop_portrait; case NoteBackground.dots: return Icons.grain; case NoteBackground.ruled: return Icons.notes; case NoteBackground.grid: return Icons.grid_4x4; case NoteBackground.cornell: return Icons.view_quilt_outlined; } } /// Paints a [NoteBackground] template behind the ink, in the page's local pixel /// space. Spacing is page-relative (a fraction of page width) so the template /// looks the same on any logical page size, and the lines are a light, subtle /// grey so they sit behind handwriting. class NoteBackgroundPainter extends CustomPainter { const NoteBackgroundPainter(this.background); final NoteBackground background; /// Pattern spacing as a fraction of the page WIDTH — a ~28-line page. static const double _spacingFraction = 1 / 28; static const Color _lineColor = Color(0x1A000000); // ~10% black, subtle grey. static const Color _dotColor = Color(0x33000000); // dots a touch darker. static const Color _accentColor = Color(0x33335C81); // Cornell margin lines. @override void paint(Canvas canvas, Size size) { if (background == NoteBackground.blank) return; final spacing = size.width * _spacingFraction; if (spacing <= 0) return; switch (background) { case NoteBackground.blank: break; case NoteBackground.dots: _paintDots(canvas, size, spacing); case NoteBackground.ruled: _paintRuled(canvas, size, spacing); case NoteBackground.grid: _paintGrid(canvas, size, spacing); case NoteBackground.cornell: _paintCornell(canvas, size, spacing); } } void _paintDots(Canvas canvas, Size size, double spacing) { final paint = Paint() ..color = _dotColor ..style = PaintingStyle.fill; final r = (spacing * 0.06).clamp(0.6, 2.0); for (double y = spacing; y < size.height; y += spacing) { for (double x = spacing; x < size.width; x += spacing) { canvas.drawCircle(Offset(x, y), r, paint); } } } void _paintRuled(Canvas canvas, Size size, double spacing) { final paint = Paint() ..color = _lineColor ..strokeWidth = 1.0; for (double y = spacing; y < size.height; y += spacing) { canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); } } void _paintGrid(Canvas canvas, Size size, double spacing) { final paint = Paint() ..color = _lineColor ..strokeWidth = 1.0; for (double y = spacing; y < size.height; y += spacing) { canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); } for (double x = spacing; x < size.width; x += spacing) { canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint); } } void _paintCornell(Canvas canvas, Size size, double spacing) { // Ruled body lines. _paintRuled(canvas, size, spacing); final accent = Paint() ..color = _accentColor ..strokeWidth = 1.4; // Left cue-column vertical line (~25% of width). final cueX = size.width * 0.25; // Bottom summary horizontal line (~80% down). final summaryY = size.height * 0.80; canvas.drawLine(Offset(cueX, 0), Offset(cueX, summaryY), accent); canvas.drawLine(Offset(0, summaryY), Offset(size.width, summaryY), accent); } @override bool shouldRepaint(covariant NoteBackgroundPainter oldDelegate) => oldDelegate.background != background; }