161 lines
5.2 KiB
Dart
161 lines
5.2 KiB
Dart
|
|
// 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;
|
||
|
|
}
|