Files
BadNote/lib/editor/canvas/note_background.dart

161 lines
5.2 KiB
Dart
Raw Normal View History

// 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;
}