534 lines
19 KiB
Dart
534 lines
19 KiB
Dart
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:flutter/services.dart';
|
||
|
|
import 'global_data.dart';
|
||
|
|
import 'models.dart';
|
||
|
|
import 'comp_15.dart';
|
||
|
|
|
||
|
|
class Comp13Screen extends StatefulWidget {
|
||
|
|
const Comp13Screen({super.key});
|
||
|
|
|
||
|
|
@override
|
||
|
|
State<Comp13Screen> createState() => _Comp13ScreenState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _Comp13ScreenState extends State<Comp13Screen> {
|
||
|
|
List<ElementoGrafico> _elementi = [];
|
||
|
|
List<TrattoPenna> _tratti = [];
|
||
|
|
String modo = 'penna';
|
||
|
|
|
||
|
|
@override
|
||
|
|
void initState() {
|
||
|
|
super.initState();
|
||
|
|
_elementi = List.from(GlobalData.elementi);
|
||
|
|
_tratti = List.from(GlobalData.tratti);
|
||
|
|
|
||
|
|
// Proviamo a chiedere il landscape al sistema (non sempre funziona su iPad)
|
||
|
|
_setLandscape();
|
||
|
|
|
||
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||
|
|
if (mounted) _mostraIstruzioni();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _setLandscape() async {
|
||
|
|
await SystemChrome.setPreferredOrientations([
|
||
|
|
DeviceOrientation.landscapeLeft,
|
||
|
|
DeviceOrientation.landscapeRight,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _setPortrait() async {
|
||
|
|
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _esci() async {
|
||
|
|
GlobalData.tratti = List.from(_tratti);
|
||
|
|
GlobalData.elementi = List.from(_elementi);
|
||
|
|
await _setPortrait();
|
||
|
|
if (mounted) Navigator.pop(context);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _vaiAvanti() async {
|
||
|
|
GlobalData.tratti = List.from(_tratti);
|
||
|
|
GlobalData.elementi = List.from(_elementi);
|
||
|
|
|
||
|
|
// Prima di cambiare pagina, rimettiamo in verticale
|
||
|
|
// await _setPortrait();
|
||
|
|
|
||
|
|
if (mounted) {
|
||
|
|
await Navigator.push(context, MaterialPageRoute(builder: (c) => const Comp15Screen()));
|
||
|
|
// Al ritorno, forziamo di nuovo orizzontale
|
||
|
|
await _setLandscape();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- FUNZIONE HELPER PER RUOTARE I DIALOGHI ---
|
||
|
|
// Se l'iPad è verticale, ruota il contenuto del dialogo di 90 gradi
|
||
|
|
Widget _ruotaSeNecessario(BuildContext context, Widget child) {
|
||
|
|
return OrientationBuilder(
|
||
|
|
builder: (context, orientation) {
|
||
|
|
return orientation == Orientation.portrait
|
||
|
|
? RotatedBox(quarterTurns: 1, child: child)
|
||
|
|
: child;
|
||
|
|
},
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _mostraIstruzioni() {
|
||
|
|
showDialog(
|
||
|
|
context: context,
|
||
|
|
builder: (ctx) => _ruotaSeNecessario(ctx, AlertDialog(
|
||
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
|
||
|
|
title: Row(
|
||
|
|
children: [
|
||
|
|
Icon(Icons.help_outline, color: Colors.blue.shade900),
|
||
|
|
const SizedBox(width: 10),
|
||
|
|
const Text("Guida al Disegno", style: TextStyle(fontWeight: FontWeight.bold)),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
content: SizedBox(
|
||
|
|
width: 500, // Larghezza fissa per evitare problemi di layout ruotato
|
||
|
|
height: 300, // Altezza fissa per scrollare comodamente
|
||
|
|
child: Scrollbar(
|
||
|
|
thumbVisibility: true,
|
||
|
|
child: SingleChildScrollView(
|
||
|
|
physics: const BouncingScrollPhysics(),
|
||
|
|
child: const Text(
|
||
|
|
"• 🖊 Usa la penna per disegnare le strade\n"
|
||
|
|
"• 🚗 🏍️ 🚛 Seleziona e tocca per aggiungere veicoli\n"
|
||
|
|
"• ↗️ Inserisci frecce di direzione\n"
|
||
|
|
"• 📝 Aggiungi testo (es. nomi vie)\n"
|
||
|
|
"• 🔄 Tocca un elemento per ruotarlo\n"
|
||
|
|
"• ❌ Premi a lungo un oggetto per eliminarlo\n"
|
||
|
|
"• 🗑️ Usa il tasto Cestino per resettare tutto",
|
||
|
|
style: TextStyle(fontSize: 16, height: 1.6, color: Colors.black87),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
actions: [
|
||
|
|
SizedBox(
|
||
|
|
width: double.infinity,
|
||
|
|
child: ElevatedButton(
|
||
|
|
style: ElevatedButton.styleFrom(
|
||
|
|
backgroundColor: Colors.blue.shade900,
|
||
|
|
foregroundColor: Colors.white,
|
||
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||
|
|
),
|
||
|
|
onPressed: () => Navigator.pop(ctx),
|
||
|
|
child: const Text("HO CAPITO", style: TextStyle(fontWeight: FontWeight.bold)),
|
||
|
|
),
|
||
|
|
)
|
||
|
|
],
|
||
|
|
)),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _gestisciInserimento(Offset pos) async {
|
||
|
|
if (modo == 'testo') {
|
||
|
|
TextEditingController tc = TextEditingController();
|
||
|
|
await showDialog(
|
||
|
|
context: context,
|
||
|
|
builder: (c) => _ruotaSeNecessario(c, Dialog(
|
||
|
|
backgroundColor: Colors.white,
|
||
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||
|
|
child: Container(
|
||
|
|
width: 300,
|
||
|
|
padding: const EdgeInsets.all(20),
|
||
|
|
child: Column(
|
||
|
|
mainAxisSize: MainAxisSize.min,
|
||
|
|
children: [
|
||
|
|
const Text("Inserisci Testo", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||
|
|
const SizedBox(height: 10),
|
||
|
|
TextField(
|
||
|
|
controller: tc,
|
||
|
|
autofocus: true,
|
||
|
|
decoration: const InputDecoration(hintText: "Nome via...", border: OutlineInputBorder())
|
||
|
|
),
|
||
|
|
const SizedBox(height: 20),
|
||
|
|
ElevatedButton(
|
||
|
|
onPressed: () {
|
||
|
|
if (tc.text.isNotEmpty) {
|
||
|
|
setState(() => _elementi.add(ElementoGrafico(pos, 'testo', label: tc.text)));
|
||
|
|
}
|
||
|
|
Navigator.pop(c);
|
||
|
|
},
|
||
|
|
child: const Text("INSERISCI")
|
||
|
|
)
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
)),
|
||
|
|
);
|
||
|
|
} else if (['auto', 'moto', 'furgone'].contains(modo)) {
|
||
|
|
int numeroVeicoli = _elementi.where((e) =>
|
||
|
|
e.tipo.startsWith('auto') ||
|
||
|
|
e.tipo.startsWith('moto') ||
|
||
|
|
e.tipo.startsWith('furgone')).length;
|
||
|
|
|
||
|
|
String prossimaLettera = String.fromCharCode(65 + numeroVeicoli);
|
||
|
|
|
||
|
|
setState(() {
|
||
|
|
_elementi.add(ElementoGrafico(pos, '$modo$prossimaLettera', label: prossimaLettera));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
// 1. Logica di orientamento principale (Body)
|
||
|
|
// Se siamo verticali, ruotiamo tutto di 90 gradi.
|
||
|
|
return PopScope(
|
||
|
|
canPop: false,
|
||
|
|
onPopInvokedWithResult: (didPop, result) async {
|
||
|
|
if (!didPop) await _esci();
|
||
|
|
},
|
||
|
|
child: OrientationBuilder(
|
||
|
|
builder: (context, orientation) {
|
||
|
|
final bool isPortrait = orientation == Orientation.portrait;
|
||
|
|
|
||
|
|
return Scaffold(
|
||
|
|
backgroundColor: Colors.white,
|
||
|
|
resizeToAvoidBottomInset: false,
|
||
|
|
// Se Portrait -> Ruota Body. Se Landscape -> Body normale.
|
||
|
|
body: isPortrait
|
||
|
|
? RotatedBox(quarterTurns: 1, child: _buildBodyContent(context))
|
||
|
|
: _buildBodyContent(context),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Contenuto principale estratto per facilitare la rotazione
|
||
|
|
Widget _buildBodyContent(BuildContext context) {
|
||
|
|
// Calcoliamo dimensioni sicure
|
||
|
|
final size = MediaQuery.of(context).size;
|
||
|
|
final double maxToolbarHeight = size.shortestSide * 0.8;
|
||
|
|
|
||
|
|
return SafeArea(
|
||
|
|
child: Stack(
|
||
|
|
children: [
|
||
|
|
Column(
|
||
|
|
children: [
|
||
|
|
Container(
|
||
|
|
height: 50,
|
||
|
|
color: Colors.blueGrey[50],
|
||
|
|
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||
|
|
child: Row(
|
||
|
|
children: [
|
||
|
|
IconButton(
|
||
|
|
icon: const Icon(Icons.arrow_back, color: Colors.black),
|
||
|
|
onPressed: _esci,
|
||
|
|
),
|
||
|
|
const Expanded(
|
||
|
|
child: Text(
|
||
|
|
"13. Grafico",
|
||
|
|
textAlign: TextAlign.center,
|
||
|
|
style: TextStyle(color: Colors.black, fontSize: 18, fontWeight: FontWeight.bold),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
IconButton(
|
||
|
|
icon: const Icon(Icons.help_outline, color: Colors.black),
|
||
|
|
onPressed: _mostraIstruzioni
|
||
|
|
),
|
||
|
|
const SizedBox(width: 15),
|
||
|
|
ElevatedButton.icon(
|
||
|
|
style: ElevatedButton.styleFrom(
|
||
|
|
backgroundColor: Colors.green,
|
||
|
|
foregroundColor: Colors.white,
|
||
|
|
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8)
|
||
|
|
),
|
||
|
|
icon: const Icon(Icons.check),
|
||
|
|
label: const Text("SALVA"),
|
||
|
|
onPressed: _vaiAvanti,
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Expanded(
|
||
|
|
child: GestureDetector(
|
||
|
|
onLongPressStart: (d) {
|
||
|
|
setState(() {
|
||
|
|
_elementi.removeWhere((e) => e.contiene(d.localPosition));
|
||
|
|
_tratti.removeWhere((t) => t.contiene(d.localPosition));
|
||
|
|
});
|
||
|
|
},
|
||
|
|
onTapDown: (d) {
|
||
|
|
bool colpito = false;
|
||
|
|
for (var e in _elementi) {
|
||
|
|
if (e.contiene(d.localPosition)) {
|
||
|
|
setState(() => e.rotazione += 0.785);
|
||
|
|
colpito = true;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!colpito && (['auto', 'moto', 'furgone', 'testo'].contains(modo))) {
|
||
|
|
_gestisciInserimento(d.localPosition);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
onPanStart: (d) {
|
||
|
|
if (modo == 'penna' || modo == 'freccia') {
|
||
|
|
setState(() => _tratti.add(TrattoPenna([d.localPosition], tipo: modo)));
|
||
|
|
}
|
||
|
|
},
|
||
|
|
onPanUpdate: (d) {
|
||
|
|
if (modo == 'penna' || modo == 'freccia') {
|
||
|
|
setState(() => _tratti.last.punti.add(d.localPosition));
|
||
|
|
}
|
||
|
|
},
|
||
|
|
child: Container(
|
||
|
|
color: Colors.white,
|
||
|
|
width: double.infinity,
|
||
|
|
height: double.infinity,
|
||
|
|
child: CustomPaint(
|
||
|
|
painter: PainterV40(_tratti, _elementi),
|
||
|
|
size: Size.infinite,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
Positioned(
|
||
|
|
left: 10, top: 60,
|
||
|
|
child: Container(
|
||
|
|
width: 55,
|
||
|
|
constraints: BoxConstraints(maxHeight: maxToolbarHeight),
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: Colors.white.withOpacity(0.95),
|
||
|
|
borderRadius: BorderRadius.circular(25),
|
||
|
|
boxShadow: [const BoxShadow(color: Colors.black26, blurRadius: 4)],
|
||
|
|
border: Border.all(color: Colors.grey.shade300),
|
||
|
|
),
|
||
|
|
child: ClipRRect(
|
||
|
|
borderRadius: BorderRadius.circular(25),
|
||
|
|
child: SingleChildScrollView(
|
||
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||
|
|
child: Column(
|
||
|
|
mainAxisSize: MainAxisSize.min,
|
||
|
|
children: [
|
||
|
|
_toolBtn(Icons.edit, 'penna', Colors.black),
|
||
|
|
_toolBtn(Icons.trending_flat, 'freccia', Colors.black),
|
||
|
|
_toolBtn(Icons.title, 'testo', Colors.black),
|
||
|
|
const Divider(indent: 8, endIndent: 8, height: 15),
|
||
|
|
_toolBtn(Icons.directions_car, 'auto', Colors.blue),
|
||
|
|
_toolBtn(Icons.two_wheeler, 'moto', Colors.orange.shade800),
|
||
|
|
_toolBtn(Icons.local_shipping, 'furgone', Colors.green),
|
||
|
|
const Divider(indent: 8, endIndent: 8, height: 15),
|
||
|
|
IconButton(
|
||
|
|
icon: const Icon(Icons.delete_forever, color: Colors.red),
|
||
|
|
tooltip: "Cancella tutto",
|
||
|
|
onPressed: () => setState(() { _tratti.clear(); _elementi.clear(); }),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Widget _toolBtn(IconData icon, String tool, Color activeColor) {
|
||
|
|
bool isSelected = modo == tool;
|
||
|
|
return Padding(
|
||
|
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||
|
|
child: Material(
|
||
|
|
color: Colors.transparent,
|
||
|
|
child: InkWell(
|
||
|
|
borderRadius: BorderRadius.circular(20),
|
||
|
|
onTap: () => setState(() => modo = tool),
|
||
|
|
child: Container(
|
||
|
|
width: 40,
|
||
|
|
height: 40,
|
||
|
|
decoration: BoxDecoration(
|
||
|
|
color: isSelected ? activeColor.withOpacity(0.15) : Colors.transparent,
|
||
|
|
border: isSelected ? Border.all(color: activeColor, width: 2) : null,
|
||
|
|
shape: BoxShape.circle,
|
||
|
|
),
|
||
|
|
child: Icon(
|
||
|
|
icon,
|
||
|
|
color: activeColor,
|
||
|
|
size: 24,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class PainterV40 extends CustomPainter {
|
||
|
|
final List<TrattoPenna> tr;
|
||
|
|
final List<ElementoGrafico> el;
|
||
|
|
PainterV40(this.tr, this.el);
|
||
|
|
|
||
|
|
final List<Color> palette = [Colors.blue, Colors.orange, Colors.green, Colors.purple, Colors.red, Colors.teal];
|
||
|
|
|
||
|
|
@override
|
||
|
|
void paint(Canvas canvas, Size size) {
|
||
|
|
Paint pStrada = Paint()..color = Colors.black..strokeWidth = 3.0..style = PaintingStyle.stroke..strokeCap = StrokeCap.round;
|
||
|
|
|
||
|
|
for (var t in tr) {
|
||
|
|
if (t.punti.length > 1) {
|
||
|
|
// Disegno il tratto stradale
|
||
|
|
Path path = Path()..moveTo(t.punti[0].dx, t.punti[0].dy);
|
||
|
|
for (var pt in t.punti) path.lineTo(pt.dx, pt.dy);
|
||
|
|
canvas.drawPath(path, pStrada);
|
||
|
|
|
||
|
|
// --- CORREZIONE FRECCIA ---
|
||
|
|
if (t.tipo == 'freccia') {
|
||
|
|
Offset pTip = t.punti.last;
|
||
|
|
Offset pBack = t.punti[t.punti.length - 2];
|
||
|
|
|
||
|
|
// STABILIZZAZIONE: Cerco un punto precedente che sia distante almeno 10 pixel
|
||
|
|
// dalla punta. Questo ignora i micro-movimenti finali del dito (jitter)
|
||
|
|
// che causavano l'inversione della freccia.
|
||
|
|
for (int i = t.punti.length - 2; i >= 0; i--) {
|
||
|
|
if ((t.punti[i] - pTip).distance > 10.0) {
|
||
|
|
pBack = t.punti[i];
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
_disegnaPunta(canvas, pBack, pTip, pStrada);
|
||
|
|
}
|
||
|
|
// --------------------------
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (var e in el) {
|
||
|
|
canvas.save();
|
||
|
|
canvas.translate(e.posizione.dx, e.posizione.dy);
|
||
|
|
canvas.rotate(e.rotazione);
|
||
|
|
|
||
|
|
if (e.tipo == 'testo') {
|
||
|
|
_disegnaTesto(canvas, e.label ?? "");
|
||
|
|
} else if (e.tipo.startsWith('auto')) {
|
||
|
|
_disegnaAuto(canvas, e);
|
||
|
|
} else if (e.tipo.startsWith('moto')) {
|
||
|
|
_disegnaMoto(canvas, e);
|
||
|
|
} else if (e.tipo.startsWith('furgone')) {
|
||
|
|
_disegnaFurgone(canvas, e);
|
||
|
|
}
|
||
|
|
canvas.restore();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ... (TUTTI GLI ALTRI METODI RESTANO IDENTICI) ...
|
||
|
|
|
||
|
|
Color _getColoreDaLettera(String lettera) {
|
||
|
|
if (lettera.isEmpty) return Colors.grey;
|
||
|
|
int idx = (lettera.codeUnitAt(0) - 65) % palette.length;
|
||
|
|
return palette[idx];
|
||
|
|
}
|
||
|
|
|
||
|
|
void _disegnaLettera(Canvas canvas, String lettera, {double fontSize = 16}) {
|
||
|
|
final tp = TextPainter(
|
||
|
|
text: TextSpan(text: lettera, style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: fontSize)),
|
||
|
|
textDirection: TextDirection.ltr
|
||
|
|
)..layout();
|
||
|
|
tp.paint(canvas, Offset(-tp.width / 2, -tp.height / 2));
|
||
|
|
}
|
||
|
|
|
||
|
|
void _disegnaAuto(Canvas canvas, ElementoGrafico e) {
|
||
|
|
String lettera = e.label ?? "A";
|
||
|
|
Color colore = _getColoreDaLettera(lettera);
|
||
|
|
double w = 48.0;
|
||
|
|
double h = 24.0;
|
||
|
|
|
||
|
|
Paint pBody = Paint()..color = colore;
|
||
|
|
Paint pBorder = Paint()..style = PaintingStyle.stroke..color = Colors.black..strokeWidth = 1.5;
|
||
|
|
|
||
|
|
RRect bodyRect = RRect.fromRectAndRadius(Rect.fromCenter(center: Offset.zero, width: w, height: h), const Radius.circular(5));
|
||
|
|
canvas.drawRRect(bodyRect, pBody);
|
||
|
|
|
||
|
|
Paint pCabin = Paint()..color = Colors.black.withOpacity(0.2);
|
||
|
|
canvas.drawRect(Rect.fromLTRB(-w/2 + 6, -h/2 + 3, w/4, h/2 - 3), pCabin);
|
||
|
|
|
||
|
|
Paint pLights = Paint()..color = Colors.yellow;
|
||
|
|
canvas.drawCircle(Offset(w/2 - 2, h/2 - 4), 2.5, pLights);
|
||
|
|
canvas.drawCircle(Offset(w/2 - 2, -h/2 + 4), 2.5, pLights);
|
||
|
|
|
||
|
|
canvas.drawRRect(bodyRect, pBorder);
|
||
|
|
_disegnaLettera(canvas, lettera);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _disegnaMoto(Canvas canvas, ElementoGrafico e) {
|
||
|
|
String lettera = e.label ?? "A";
|
||
|
|
Color colore = _getColoreDaLettera(lettera);
|
||
|
|
|
||
|
|
double len = 40.0;
|
||
|
|
double wid = 14.0;
|
||
|
|
|
||
|
|
Paint pBody = Paint()..color = colore;
|
||
|
|
Paint pBlack = Paint()..color = Colors.black;
|
||
|
|
Paint pBorder = Paint()..style = PaintingStyle.stroke..color = Colors.black..strokeWidth = 1.0;
|
||
|
|
|
||
|
|
canvas.drawCircle(Offset(-len/2 + 4, 0), 5.0, pBlack);
|
||
|
|
canvas.drawCircle(Offset(len/2 - 4, 0), 5.0, pBlack);
|
||
|
|
|
||
|
|
Rect bodyRect = Rect.fromCenter(center: Offset.zero, width: len - 10, height: wid - 4);
|
||
|
|
canvas.drawRRect(RRect.fromRectAndRadius(bodyRect, const Radius.circular(4)), pBody);
|
||
|
|
canvas.drawRRect(RRect.fromRectAndRadius(bodyRect, const Radius.circular(4)), pBorder);
|
||
|
|
|
||
|
|
Rect sellaRect = Rect.fromCenter(center: Offset(-5, 0), width: 12, height: 8);
|
||
|
|
canvas.drawRect(sellaRect, pBlack);
|
||
|
|
|
||
|
|
Paint pManubrio = Paint()..color = Colors.black..strokeWidth = 3.0..strokeCap = StrokeCap.round;
|
||
|
|
double xHandle = len/2 - 12;
|
||
|
|
canvas.drawLine(Offset(xHandle, -10), Offset(xHandle, 10), pManubrio);
|
||
|
|
|
||
|
|
Paint pLights = Paint()..color = Colors.yellow;
|
||
|
|
canvas.drawCircle(Offset(len/2, 0), 3.0, pLights);
|
||
|
|
|
||
|
|
_disegnaLettera(canvas, lettera, fontSize: 10);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _disegnaFurgone(Canvas canvas, ElementoGrafico e) {
|
||
|
|
String lettera = e.label ?? "A";
|
||
|
|
Color colore = _getColoreDaLettera(lettera);
|
||
|
|
double w = 60.0;
|
||
|
|
double h = 26.0;
|
||
|
|
|
||
|
|
Paint pBody = Paint()..color = colore;
|
||
|
|
Paint pBorder = Paint()..style = PaintingStyle.stroke..color = Colors.black..strokeWidth = 1.5;
|
||
|
|
|
||
|
|
Rect caricoRect = Rect.fromLTRB(-w/2, -h/2, w/4, h/2);
|
||
|
|
canvas.drawRect(caricoRect, pBody);
|
||
|
|
canvas.drawRect(caricoRect, pBorder);
|
||
|
|
|
||
|
|
Rect cabinaRect = Rect.fromLTRB(w/4, -h/2 + 1, w/2, h/2 - 1);
|
||
|
|
canvas.drawRect(cabinaRect, pBody);
|
||
|
|
canvas.drawRect(cabinaRect, pBorder);
|
||
|
|
|
||
|
|
Paint pVetro = Paint()..color = Colors.black.withOpacity(0.3);
|
||
|
|
canvas.drawRect(Rect.fromLTRB(w/4 + 2, -h/2 + 3, w/2 - 2, h/2 - 3), pVetro);
|
||
|
|
|
||
|
|
Paint pLights = Paint()..color = Colors.yellow;
|
||
|
|
canvas.drawRect(Rect.fromLTWH(w/2 - 2, -h/2 + 2, 2, 4), pLights);
|
||
|
|
canvas.drawRect(Rect.fromLTWH(w/2 - 2, h/2 - 6, 2, 4), pLights);
|
||
|
|
|
||
|
|
_disegnaLettera(canvas, lettera);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _disegnaTesto(Canvas canvas, String txt) {
|
||
|
|
final tp = TextPainter(
|
||
|
|
text: TextSpan(text: txt, style: const TextStyle(color: Colors.black, fontSize: 20, fontWeight: FontWeight.bold)),
|
||
|
|
textDirection: TextDirection.ltr
|
||
|
|
)..layout();
|
||
|
|
tp.paint(canvas, Offset(-tp.width/2, -tp.height/2));
|
||
|
|
}
|
||
|
|
|
||
|
|
void _disegnaPunta(Canvas canvas, Offset p1, Offset p2, Paint paint) {
|
||
|
|
double angle = (p2 - p1).direction;
|
||
|
|
canvas.drawLine(p2, p2 - Offset.fromDirection(angle - 0.5, 10), paint);
|
||
|
|
canvas.drawLine(p2, p2 - Offset.fromDirection(angle + 0.5, 10), paint);
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
|
||
|
|
}
|