outsource line drawing in paint controller
This commit is contained in:
parent
be3b54f258
commit
4c54265f89
@ -1,17 +1,15 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:adwaita_icons/adwaita_icons.dart';
|
|
||||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:iconify_flutter/iconify_flutter.dart';
|
import 'my_painter.dart';
|
||||||
import 'package:iconify_flutter/icons/emojione_monotone.dart';
|
import 'paint_controller.dart';
|
||||||
import 'package:iconify_flutter/icons/jam.dart';
|
import 'screen_document_mapping.dart';
|
||||||
import 'package:notes/canvas/my_painter.dart';
|
|
||||||
import 'package:notes/canvas/screen_document_mapping.dart';
|
|
||||||
|
|
||||||
import '../icon_material_button.dart';
|
import '../icon_material_button.dart';
|
||||||
import 'document_types.dart';
|
import '../tool_bar.dart';
|
||||||
|
|
||||||
|
/// Handles input events and draws canvas element
|
||||||
class DrawingPage extends StatefulWidget {
|
class DrawingPage extends StatefulWidget {
|
||||||
const DrawingPage({Key? key}) : super(key: key);
|
const DrawingPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -20,15 +18,11 @@ class DrawingPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DrawingPageState extends State<DrawingPage> {
|
class _DrawingPageState extends State<DrawingPage> {
|
||||||
List<Stroke> _strokes = [];
|
|
||||||
bool allowDrawWithFinger = false;
|
|
||||||
|
|
||||||
double zoom = .75;
|
double zoom = .75;
|
||||||
double basezoom = 1.0;
|
double basezoom = 1.0;
|
||||||
Offset offset = const Offset(.0, .0);
|
Offset offset = const Offset(.0, .0);
|
||||||
|
|
||||||
// todo better pen system
|
PaintController controller = PaintController();
|
||||||
bool eraseractive = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -37,7 +31,7 @@ class _DrawingPageState extends State<DrawingPage> {
|
|||||||
// todo might be weird behaviour if used with short side
|
// todo might be weird behaviour if used with short side
|
||||||
final screenWidth =
|
final screenWidth =
|
||||||
(window.physicalSize.longestSide / window.devicePixelRatio);
|
(window.physicalSize.longestSide / window.devicePixelRatio);
|
||||||
_calcNewPageOffset(const Offset(.0, .0), screenWidth);
|
_calcNewPageOffset(const Offset(.0, .0), screenWidth - 45);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -47,68 +41,39 @@ class _DrawingPageState extends State<DrawingPage> {
|
|||||||
IconMaterialButton(
|
IconMaterialButton(
|
||||||
icon: const Icon(FluentIcons.book_open_48_filled),
|
icon: const Icon(FluentIcons.book_open_48_filled),
|
||||||
color: const Color.fromRGBO(255, 255, 255, .85),
|
color: const Color.fromRGBO(255, 255, 255, .85),
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
// todo implement
|
||||||
|
},
|
||||||
),
|
),
|
||||||
IconMaterialButton(
|
IconMaterialButton(
|
||||||
icon: const Icon(FluentIcons.document_one_page_24_regular),
|
icon: const Icon(FluentIcons.document_one_page_24_regular),
|
||||||
color: const Color.fromRGBO(255, 255, 255, .85),
|
color: const Color.fromRGBO(255, 255, 255, .85),
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
// todo implement
|
||||||
|
},
|
||||||
),
|
),
|
||||||
IconMaterialButton(
|
IconMaterialButton(
|
||||||
icon: const Icon(Icons.attachment_outlined),
|
icon: const Icon(Icons.attachment_outlined),
|
||||||
color: const Color.fromRGBO(255, 255, 255, .85),
|
color: const Color.fromRGBO(255, 255, 255, .85),
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
// todo implement
|
||||||
|
},
|
||||||
rotation: -pi / 4,
|
rotation: -pi / 4,
|
||||||
),
|
),
|
||||||
IconMaterialButton(
|
IconMaterialButton(
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(Icons.more_vert),
|
||||||
color: const Color.fromRGBO(255, 255, 255, .85),
|
color: const Color.fromRGBO(255, 255, 255, .85),
|
||||||
onPressed: () {},
|
onPressed: () {
|
||||||
|
// todo implement
|
||||||
|
},
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
body: Row(
|
body: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
ToolBar(
|
||||||
color: const Color(0xff3f3f3f),
|
onPenChange: (pen) {
|
||||||
width: 45,
|
controller.changePen(pen);
|
||||||
child: Column(
|
},
|
||||||
children: [
|
|
||||||
const SizedBox(
|
|
||||||
height: 10,
|
|
||||||
),
|
|
||||||
IconMaterialButton(
|
|
||||||
icon: const Iconify(EmojioneMonotone.fountain_pen, color: Color.fromRGBO(255, 255, 255, .85),),
|
|
||||||
color: const Color.fromRGBO(255, 255, 255, .85),
|
|
||||||
onPressed: () => setState(() => eraseractive = false),
|
|
||||||
selected: !eraseractive,
|
|
||||||
iconSize: 24,
|
|
||||||
),
|
|
||||||
IconMaterialButton(
|
|
||||||
icon: const Iconify(Jam.highlighter, color: Color.fromRGBO(255, 255, 255, .85),),
|
|
||||||
color: const Color.fromRGBO(255, 255, 255, .85),
|
|
||||||
onPressed: () => setState(() => eraseractive = false),
|
|
||||||
selected: false,
|
|
||||||
iconSize: 24,
|
|
||||||
),
|
|
||||||
IconMaterialButton(
|
|
||||||
icon: Transform.translate(
|
|
||||||
offset: const Offset(-2.0, .0),
|
|
||||||
child: const AdwaitaIcon(AdwaitaIcons.eraser2),
|
|
||||||
),
|
|
||||||
color: const Color.fromRGBO(255, 255, 255, .85),
|
|
||||||
onPressed: () => setState(() => eraseractive = true),
|
|
||||||
iconSize: 24,
|
|
||||||
selected: eraseractive,
|
|
||||||
),
|
|
||||||
IconMaterialButton(
|
|
||||||
icon: const Icon(FluentIcons.select_object_24_regular),
|
|
||||||
color: const Color.fromRGBO(255, 255, 255, .85),
|
|
||||||
onPressed: () => setState(() => eraseractive = false),
|
|
||||||
selected: false,
|
|
||||||
iconSize: 24,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Expanded(child: RepaintBoundary(child: _buildCanvas())),
|
Expanded(child: RepaintBoundary(child: _buildCanvas())),
|
||||||
],
|
],
|
||||||
@ -116,28 +81,6 @@ class _DrawingPageState extends State<DrawingPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _calcTiltedWidth(double baseWidth, double tilt) {
|
|
||||||
if (tilt == .0) return baseWidth;
|
|
||||||
return baseWidth * tilt;
|
|
||||||
}
|
|
||||||
|
|
||||||
double _calcAngleDependentWidth(Point pt1, Point pt2, double basetickness) {
|
|
||||||
double dx = pt2.point.dx - pt1.point.dx;
|
|
||||||
double dy = pt2.point.dy - pt1.point.dy;
|
|
||||||
|
|
||||||
// todo those deltas to small to get an accurate direction!
|
|
||||||
|
|
||||||
double alpha = atan(dx / dy);
|
|
||||||
// alpha has range from 0 - 2pi
|
|
||||||
// we want 0.5 -1;
|
|
||||||
|
|
||||||
alpha /= (2 * pi * 2);
|
|
||||||
alpha += .5;
|
|
||||||
|
|
||||||
double thickness = basetickness * alpha;
|
|
||||||
return thickness;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate new page offset from mousepointer delta
|
// calculate new page offset from mousepointer delta
|
||||||
void _calcNewPageOffset(Offset delta, double canvasWidth) {
|
void _calcNewPageOffset(Offset delta, double canvasWidth) {
|
||||||
if (zoom > 1.0) {
|
if (zoom > 1.0) {
|
||||||
@ -175,96 +118,28 @@ class _DrawingPageState extends State<DrawingPage> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo outsource this eraser pen
|
controller.pointMoveEvent(pos, event.kind, event.tilt);
|
||||||
if (eraseractive) {
|
|
||||||
// todo dynamic eraser size
|
|
||||||
final eraserrect = Rect.fromCircle(center: pos, radius: 3);
|
|
||||||
for (final stroke in _strokes) {
|
|
||||||
// check if delete action was within bounding rect of stroke
|
|
||||||
if (stroke.getBoundingRect().contains(pos)) {
|
|
||||||
// check if eraser hit an point within its range
|
|
||||||
for (final pt in stroke.points) {
|
|
||||||
if (eraserrect.contains(pt.point)) {
|
|
||||||
setState(() {
|
|
||||||
_strokes = List.from(_strokes)..remove(stroke);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allowDrawWithFinger || event.kind != PointerDeviceKind.touch) {
|
if (event.kind == PointerDeviceKind.touch) {
|
||||||
final pts = _strokes.last.points;
|
|
||||||
|
|
||||||
if (pts.last.point == pos) return;
|
|
||||||
|
|
||||||
double newWidth = _calcTiltedWidth(5.0, event.tilt);
|
|
||||||
if (_strokes.last.points.length > 1) {
|
|
||||||
newWidth =
|
|
||||||
_calcAngleDependentWidth(pts.last, pts[pts.length - 2], newWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_strokes = List.from(_strokes, growable: false)
|
|
||||||
..last.addPoint(Point(pos, newWidth));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
_calcNewPageOffset(event.delta, size.width);
|
_calcNewPageOffset(event.delta, size.width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCanvas() {
|
Widget _buildCanvas() {
|
||||||
final size = MediaQuery.of(context).size;
|
final size = MediaQuery.of(context).size;
|
||||||
|
|
||||||
final canvasSize = Size(size.width - 45, size.height);
|
final canvasSize = Size(size.width - 45, size.height);
|
||||||
|
|
||||||
return Listener(
|
return Listener(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onPointerMove: (e) => _onPointerMove(e, size),
|
onPointerMove: (e) => _onPointerMove(e, canvasSize),
|
||||||
onPointerSignal: (event) {
|
|
||||||
print('Button: ${event.buttons}');
|
|
||||||
},
|
|
||||||
onPointerDown: (event) {
|
onPointerDown: (event) {
|
||||||
print('Button: ${event.buttons}');
|
Offset pos = event.localPosition;
|
||||||
|
final scale = calcPageDependentScale(zoom, a4Page, canvasSize);
|
||||||
if (allowDrawWithFinger || event.kind != PointerDeviceKind.touch) {
|
pos = translateScreenToDocumentPoint(pos, scale, offset);
|
||||||
Offset pos = event.localPosition;
|
controller.pointDownEvent(pos, event.kind, event.tilt);
|
||||||
final scale = calcPageDependentScale(zoom, a4Page, size);
|
|
||||||
pos = translateScreenToDocumentPoint(pos, scale, offset);
|
|
||||||
|
|
||||||
// todo line drawn on edge where line left page
|
|
||||||
if (!a4Page.contains(pos)) return;
|
|
||||||
|
|
||||||
if (eraseractive) return;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_strokes = List.from(_strokes)
|
|
||||||
..add(Stroke.fromPoints(
|
|
||||||
[Point(pos, _calcTiltedWidth(3.0, event.tilt))]));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onPointerUp: (event) {
|
onPointerUp: (event) {
|
||||||
if (eraseractive) return;
|
controller.pointUpEvent(event.kind);
|
||||||
|
|
||||||
if (allowDrawWithFinger || event.kind != PointerDeviceKind.touch) {
|
|
||||||
if (_strokes.last.points.length <= 1) {
|
|
||||||
// if the line consists only of one point (point) add endpoint as the same to allow drawing a line
|
|
||||||
// todo maybe solve this in custompainter in future
|
|
||||||
setState(() {
|
|
||||||
_strokes = List.from(_strokes, growable: false)
|
|
||||||
..last.points.add(_strokes.last.points.last);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
print(_strokes.length);
|
|
||||||
print(_strokes.last.points.length);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onScaleUpdate: (details) {
|
onScaleUpdate: (details) {
|
||||||
@ -277,18 +152,12 @@ class _DrawingPageState extends State<DrawingPage> {
|
|||||||
onScaleEnd: (details) {
|
onScaleEnd: (details) {
|
||||||
basezoom = zoom;
|
basezoom = zoom;
|
||||||
},
|
},
|
||||||
onSecondaryTap: () {
|
|
||||||
print('secctab');
|
|
||||||
},
|
|
||||||
onTertiaryTapDown: (details) {
|
|
||||||
print('tertiary button');
|
|
||||||
},
|
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: MyPainter(
|
painter: MyPainter(
|
||||||
strokes: _strokes,
|
|
||||||
offset: offset,
|
offset: offset,
|
||||||
zoom: zoom,
|
zoom: zoom,
|
||||||
canvasSize: canvasSize),
|
canvasSize: canvasSize,
|
||||||
|
controller: controller),
|
||||||
size: canvasSize,
|
size: canvasSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,24 +1,32 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:notes/canvas/paint_controller.dart';
|
||||||
import 'package:notes/canvas/screen_document_mapping.dart';
|
import 'package:notes/canvas/screen_document_mapping.dart';
|
||||||
|
|
||||||
import 'document_types.dart';
|
|
||||||
|
|
||||||
final Rect a4Page =
|
final Rect a4Page =
|
||||||
Rect.fromPoints(const Offset(.0, .0), const Offset(210, 210 * sqrt2));
|
Rect.fromPoints(const Offset(.0, .0), const Offset(210, 210 * sqrt2));
|
||||||
|
|
||||||
class MyPainter extends CustomPainter {
|
class MyPainter extends CustomPainter {
|
||||||
List<Stroke> strokes;
|
|
||||||
double zoom;
|
double zoom;
|
||||||
Offset offset;
|
Offset offset;
|
||||||
Size canvasSize;
|
Size canvasSize;
|
||||||
|
PaintController controller;
|
||||||
|
|
||||||
|
late Pen activePen;
|
||||||
|
|
||||||
MyPainter(
|
MyPainter(
|
||||||
{required this.strokes,
|
{required this.zoom,
|
||||||
required this.zoom,
|
|
||||||
required this.offset,
|
required this.offset,
|
||||||
required this.canvasSize});
|
required this.canvasSize,
|
||||||
|
required this.controller})
|
||||||
|
: super(repaint: controller) {
|
||||||
|
activePen = controller.activePen;
|
||||||
|
|
||||||
|
controller.addListener(() {
|
||||||
|
activePen = controller.activePen;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Offset _translatept(Offset pt, Size canvasSize) {
|
Offset _translatept(Offset pt, Size canvasSize) {
|
||||||
final scale = calcPageDependentScale(zoom, a4Page, canvasSize);
|
final scale = calcPageDependentScale(zoom, a4Page, canvasSize);
|
||||||
@ -42,7 +50,7 @@ class MyPainter extends CustomPainter {
|
|||||||
_translatept(a4Page.bottomRight, size)),
|
_translatept(a4Page.bottomRight, size)),
|
||||||
backgroundPaint);
|
backgroundPaint);
|
||||||
|
|
||||||
for (final stroke in strokes) {
|
for (final stroke in controller.strokes) {
|
||||||
for (int i = 0; i < stroke.points.length - 1; i++) {
|
for (int i = 0; i < stroke.points.length - 1; i++) {
|
||||||
Offset pt1 = stroke.points[i].point;
|
Offset pt1 = stroke.points[i].point;
|
||||||
pt1 = _translatept(pt1, size);
|
pt1 = _translatept(pt1, size);
|
||||||
@ -58,8 +66,6 @@ class MyPainter extends CustomPainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRepaint(MyPainter oldDelegate) {
|
bool shouldRepaint(MyPainter oldDelegate) {
|
||||||
return oldDelegate.strokes != strokes ||
|
return oldDelegate.zoom != zoom || oldDelegate.offset != offset;
|
||||||
oldDelegate.zoom != zoom ||
|
|
||||||
oldDelegate.offset != offset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
116
lib/canvas/paint_controller.dart
Normal file
116
lib/canvas/paint_controller.dart
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'document_types.dart';
|
||||||
|
import 'my_painter.dart';
|
||||||
|
|
||||||
|
enum Pen { eraser, pen, highlighter, selector }
|
||||||
|
|
||||||
|
class PaintController extends ChangeNotifier {
|
||||||
|
Pen activePen = Pen.pen;
|
||||||
|
List<Stroke> strokes = [];
|
||||||
|
final bool _allowDrawWithFinger = false;
|
||||||
|
|
||||||
|
void changePen(Pen pen) {
|
||||||
|
activePen = pen;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
double _calcTiltedWidth(double baseWidth, double tilt) {
|
||||||
|
if (tilt == .0) return baseWidth;
|
||||||
|
return baseWidth * tilt;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _calcAngleDependentWidth(Point pt1, Point pt2, double basetickness) {
|
||||||
|
double dx = pt2.point.dx - pt1.point.dx;
|
||||||
|
double dy = pt2.point.dy - pt1.point.dy;
|
||||||
|
|
||||||
|
// todo those deltas to small to get an accurate direction!
|
||||||
|
|
||||||
|
double alpha = atan(dx / dy);
|
||||||
|
// alpha has range from 0 - 2pi
|
||||||
|
// we want 0.5 -1;
|
||||||
|
|
||||||
|
alpha /= (2 * pi * 2);
|
||||||
|
alpha += .5;
|
||||||
|
|
||||||
|
double thickness = basetickness * alpha;
|
||||||
|
return thickness;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointDownEvent(Offset offset, PointerDeviceKind pointer, double tilt) {
|
||||||
|
if (_allowDrawWithFinger || pointer != PointerDeviceKind.touch) {
|
||||||
|
// todo line drawn on edge where line left page
|
||||||
|
if (!a4Page.contains(offset)) return;
|
||||||
|
|
||||||
|
// todo handle other pens
|
||||||
|
if (activePen != Pen.pen) return;
|
||||||
|
|
||||||
|
strokes
|
||||||
|
.add(Stroke.fromPoints([Point(offset, _calcTiltedWidth(3.0, tilt))]));
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointUpEvent(PointerDeviceKind pointer) {
|
||||||
|
if (activePen == Pen.eraser) return;
|
||||||
|
|
||||||
|
if (_allowDrawWithFinger || pointer != PointerDeviceKind.touch) {
|
||||||
|
if (strokes.last.points.length <= 1) {
|
||||||
|
// if the line consists only of one point (point) add endpoint as the same to allow drawing a line
|
||||||
|
// todo maybe solve this in custompainter in future
|
||||||
|
strokes.last.points.add(strokes.last.points.last);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pointMoveEvent(Offset offset, PointerDeviceKind pointer, double tilt) {
|
||||||
|
if (!a4Page.contains(offset)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_allowDrawWithFinger || pointer != PointerDeviceKind.touch) {
|
||||||
|
switch (activePen) {
|
||||||
|
case Pen.eraser:
|
||||||
|
// todo dynamic eraser size
|
||||||
|
final eraserrect = Rect.fromCircle(center: offset, radius: 3);
|
||||||
|
for (final stroke in strokes) {
|
||||||
|
// check if delete action was within bounding rect of stroke
|
||||||
|
if (stroke.getBoundingRect().contains(offset)) {
|
||||||
|
// check if eraser hit an point within its range
|
||||||
|
for (final pt in stroke.points) {
|
||||||
|
if (eraserrect.contains(pt.point)) {
|
||||||
|
strokes.remove(stroke);
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Pen.pen:
|
||||||
|
final pts = strokes.last.points;
|
||||||
|
if (pts.last.point == offset) return;
|
||||||
|
|
||||||
|
double newWidth = _calcTiltedWidth(5.0, tilt);
|
||||||
|
if (strokes.last.points.length > 1) {
|
||||||
|
newWidth = _calcAngleDependentWidth(
|
||||||
|
pts.last, pts[pts.length - 2], newWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
strokes.last.addPoint(Point(offset, newWidth));
|
||||||
|
break;
|
||||||
|
case Pen.highlighter:
|
||||||
|
// TODO: Handle this case.
|
||||||
|
break;
|
||||||
|
case Pen.selector:
|
||||||
|
// TODO: Handle this case.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
lib/tool_bar.dart
Normal file
86
lib/tool_bar.dart
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import 'package:adwaita_icons/adwaita_icons.dart';
|
||||||
|
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:iconify_flutter/iconify_flutter.dart';
|
||||||
|
import 'package:iconify_flutter/icons/emojione_monotone.dart';
|
||||||
|
import 'package:iconify_flutter/icons/jam.dart';
|
||||||
|
|
||||||
|
import 'canvas/paint_controller.dart';
|
||||||
|
import 'icon_material_button.dart';
|
||||||
|
|
||||||
|
class ToolBar extends StatefulWidget {
|
||||||
|
const ToolBar({Key? key, required this.onPenChange}) : super(key: key);
|
||||||
|
|
||||||
|
final void Function(Pen pen) onPenChange;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ToolBar> createState() => _ToolBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ToolBarState extends State<ToolBar> {
|
||||||
|
Pen activepen = Pen.pen;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
color: const Color(0xff3f3f3f),
|
||||||
|
width: 45,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
IconMaterialButton(
|
||||||
|
icon: const Iconify(
|
||||||
|
EmojioneMonotone.fountain_pen,
|
||||||
|
color: Color.fromRGBO(255, 255, 255, .85),
|
||||||
|
),
|
||||||
|
color: const Color.fromRGBO(255, 255, 255, .85),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => activepen = Pen.pen);
|
||||||
|
widget.onPenChange(Pen.pen);
|
||||||
|
},
|
||||||
|
selected: activepen == Pen.pen,
|
||||||
|
iconSize: 24,
|
||||||
|
),
|
||||||
|
IconMaterialButton(
|
||||||
|
icon: const Iconify(
|
||||||
|
Jam.highlighter,
|
||||||
|
color: Color.fromRGBO(255, 255, 255, .85),
|
||||||
|
),
|
||||||
|
color: const Color.fromRGBO(255, 255, 255, .85),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => activepen = Pen.highlighter);
|
||||||
|
widget.onPenChange(Pen.highlighter);
|
||||||
|
},
|
||||||
|
selected: activepen == Pen.highlighter,
|
||||||
|
iconSize: 24,
|
||||||
|
),
|
||||||
|
IconMaterialButton(
|
||||||
|
icon: Transform.translate(
|
||||||
|
offset: const Offset(-2.0, .0),
|
||||||
|
child: const AdwaitaIcon(AdwaitaIcons.eraser2),
|
||||||
|
),
|
||||||
|
color: const Color.fromRGBO(255, 255, 255, .85),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => activepen = Pen.eraser);
|
||||||
|
widget.onPenChange(Pen.eraser);
|
||||||
|
},
|
||||||
|
iconSize: 24,
|
||||||
|
selected: activepen == Pen.eraser,
|
||||||
|
),
|
||||||
|
IconMaterialButton(
|
||||||
|
icon: const Icon(FluentIcons.select_object_24_regular),
|
||||||
|
color: const Color.fromRGBO(255, 255, 255, .85),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => activepen = Pen.selector);
|
||||||
|
widget.onPenChange(Pen.selector);
|
||||||
|
},
|
||||||
|
selected: activepen == Pen.selector,
|
||||||
|
iconSize: 24,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user