Line data Source code
1 : import 'dart:convert';
2 :
3 : import 'package:flutter/material.dart';
4 : import 'package:flutter/services.dart';
5 : import 'package:flutter_ume/util/constants.dart';
6 : import 'package:flutter_ume_kit_ui/components/hit_test.dart';
7 : import 'package:flutter_ume/flutter_ume.dart';
8 : import 'icon.dart' as icon;
9 :
10 : class AlignRuler extends StatefulWidget implements Pluggable {
11 2 : AlignRuler({Key? key}) : super(key: key);
12 :
13 1 : @override
14 1 : _AlignRulerState createState() => _AlignRulerState();
15 :
16 1 : @override
17 : Widget buildWidget(BuildContext? context) => this;
18 :
19 1 : @override
20 : ImageProvider<Object> get iconImageProvider =>
21 2 : MemoryImage(base64Decode(icon.iconData));
22 :
23 1 : @override
24 : String get name => 'AlignRuler';
25 :
26 0 : @override
27 : String get displayName => 'AlignRuler';
28 :
29 1 : @override
30 : void onTrigger() {}
31 : }
32 :
33 : class _AlignRulerState extends State<AlignRuler> {
34 : Size _windowSize = windowSize;
35 : final Size _dotSize = Size(80, 80);
36 : Offset _dotPosition = Offset.zero;
37 : BorderRadius? _radius;
38 : late Offset _dotOffset;
39 : final TextStyle _fontStyle = TextStyle(color: Colors.red, fontSize: 15);
40 : Size _textSize = Size.zero;
41 : double _toolBarY = 60.0;
42 : bool _switched = false;
43 : InspectorSelection _selection = WidgetInspectorService.instance.selection;
44 :
45 1 : @override
46 : void initState() {
47 3 : _dotPosition = _windowSize.center(Offset.zero);
48 4 : _radius = BorderRadius.circular(_dotSize.longestSide);
49 3 : _dotOffset = _dotSize.center(Offset.zero);
50 1 : super.initState();
51 2 : _textSize = _getTextSize();
52 2 : _selection.clear();
53 : }
54 :
55 1 : void _onPanUpdate(DragUpdateDetails dragDetails) {
56 2 : setState(() {
57 2 : _dotPosition = dragDetails.globalPosition;
58 : });
59 : }
60 :
61 1 : void _onPanEnd(DragEndDetails dragDetails) {
62 1 : if (!_switched) return;
63 2 : final List<RenderObject> objects = HitTest.hitTest(_dotPosition);
64 2 : _selection.candidates = objects;
65 : Offset offset = Offset.zero;
66 2 : for (var obj in objects) {
67 2 : var translation = obj.getTransformTo(null).getTranslation();
68 5 : Rect rect = obj.paintBounds.shift(Offset(translation.x, translation.y));
69 2 : if (rect.contains(_dotPosition)) {
70 : double dx, dy = 0.0;
71 2 : double perW = rect.width / 2;
72 2 : double perH = rect.height / 2;
73 5 : if (_dotPosition.dx <= perW + translation.x) {
74 1 : dx = translation.x;
75 : } else {
76 0 : dx = translation.x + rect.width;
77 : }
78 5 : if (_dotPosition.dy <= translation.y + perH) {
79 0 : dy = translation.y;
80 : } else {
81 3 : dy = translation.y + rect.height;
82 : }
83 1 : offset = Offset(dx, dy);
84 : break;
85 : }
86 : }
87 2 : setState(() {
88 2 : _dotPosition = offset == Offset.zero ? _dotPosition : offset;
89 1 : HapticFeedback.mediumImpact();
90 : });
91 : }
92 :
93 0 : void _toolBarPanUpdate(DragUpdateDetails dragDetails) {
94 0 : setState(() {
95 0 : _toolBarY = dragDetails.globalPosition.dy - 40;
96 : });
97 : }
98 :
99 1 : Size _getTextSize() {
100 1 : final textPainter = TextPainter(
101 : textDirection: TextDirection.ltr,
102 1 : text: TextSpan(
103 : text: '789.5', // for caculate size
104 1 : style: _fontStyle,
105 : ),
106 : );
107 1 : textPainter.layout();
108 3 : return Size(textPainter.width, textPainter.height);
109 : }
110 :
111 1 : void _switchChanged(bool swi) {
112 2 : setState(() {
113 1 : _switched = swi;
114 1 : if (!_switched) {
115 0 : _selection.clear();
116 : }
117 : });
118 : }
119 :
120 1 : @override
121 : Widget build(BuildContext context) {
122 2 : if (_windowSize.isEmpty) {
123 0 : _windowSize = MediaQuery.of(context).size;
124 0 : _dotPosition = _windowSize.center(Offset.zero);
125 : }
126 : const TextStyle style = TextStyle(fontSize: 17, color: Colors.black);
127 1 : Widget toolBar = Container(
128 4 : width: MediaQuery.of(context).size.width - 32,
129 1 : decoration: BoxDecoration(
130 : color: Colors.white,
131 1 : borderRadius: BorderRadius.circular(16),
132 1 : boxShadow: [
133 : const BoxShadow(
134 : color: Colors.black26, blurRadius: 6, offset: Offset(2, 2))
135 : ]),
136 : padding: const EdgeInsets.only(bottom: 16, top: 12),
137 1 : child: Column(
138 : crossAxisAlignment: CrossAxisAlignment.start,
139 1 : children: <Widget>[
140 1 : Padding(
141 : padding: const EdgeInsets.only(left: 26, right: 26),
142 1 : child: Row(
143 : mainAxisAlignment: MainAxisAlignment.spaceAround,
144 1 : children: <Widget>[
145 1 : Expanded(
146 1 : child: Column(
147 : crossAxisAlignment: CrossAxisAlignment.start,
148 1 : children: <Widget>[
149 5 : Text('Left: ${_dotPosition.dx.toStringAsFixed(1)}',
150 : style: style),
151 1 : Padding(padding: const EdgeInsets.only(top: 8)),
152 1 : Text(
153 7 : 'Right: ${(_windowSize.width - _dotPosition.dx).toStringAsFixed(1)}',
154 : style: style),
155 : ],
156 : ),
157 : ),
158 1 : Expanded(
159 1 : child: Column(
160 : crossAxisAlignment: CrossAxisAlignment.start,
161 1 : children: <Widget>[
162 5 : Text('Top: ${_dotPosition.dy.toStringAsFixed(1)}',
163 : style: style),
164 1 : Padding(padding: const EdgeInsets.only(top: 8)),
165 1 : Text(
166 7 : 'Bottom: ${(_windowSize.height - _dotPosition.dy).toStringAsFixed(1)}',
167 : style: style),
168 : ],
169 : ),
170 : ),
171 : ],
172 : ),
173 : ),
174 1 : Padding(
175 : padding: const EdgeInsets.only(top: 12),
176 1 : child: Row(
177 1 : children: <Widget>[
178 1 : Container(
179 : padding: const EdgeInsets.only(left: 20),
180 : height: 30,
181 1 : child: Transform.scale(
182 : scale: 1.3,
183 1 : child: Switch(
184 1 : value: _switched,
185 1 : onChanged: _switchChanged,
186 : activeColor: Colors.red),
187 : ),
188 : ),
189 1 : Expanded(
190 1 : child: Padding(
191 : padding: const EdgeInsets.only(left: 10),
192 1 : child: Text('开启后松手将会自动吸附至最近widget',
193 : style: const TextStyle(
194 : color: Colors.red, fontWeight: FontWeight.w500)),
195 : ),
196 : )
197 : ],
198 : ),
199 : )
200 : ],
201 : ),
202 : );
203 :
204 5 : double verticalLeft = _dotPosition.dx - _textSize.width;
205 5 : double horizontalTop = _dotPosition.dy - _textSize.height;
206 :
207 1 : return Container(
208 : color: Colors.transparent,
209 3 : height: MediaQuery.of(context).size.height,
210 3 : width: MediaQuery.of(context).size.height,
211 1 : child: Stack(
212 : alignment: Alignment.center,
213 1 : children: [
214 1 : Positioned(
215 : top: horizontalTop,
216 7 : left: _dotPosition.dx / 2 - _textSize.width / 2,
217 5 : child: Text('${_dotPosition.dx.toStringAsFixed(1)}',
218 1 : style: _fontStyle)),
219 1 : Positioned(
220 : left: verticalLeft,
221 7 : top: _dotPosition.dy / 2 - _textSize.height / 2,
222 5 : child: Text('${_dotPosition.dy.toStringAsFixed(1)}',
223 1 : style: _fontStyle)),
224 1 : Positioned(
225 3 : left: _dotPosition.dx +
226 7 : (_windowSize.width - _dotPosition.dx) / 2 -
227 3 : _textSize.width / 2,
228 : top: horizontalTop,
229 1 : child: Text(
230 7 : '${(_windowSize.width - _dotPosition.dx).toStringAsFixed(1)}',
231 1 : style: _fontStyle)),
232 1 : Positioned(
233 3 : top: _dotPosition.dy +
234 7 : (_windowSize.height - _dotPosition.dy) / 2 -
235 3 : _textSize.height / 2,
236 : left: verticalLeft,
237 1 : child: Text(
238 7 : '${(_windowSize.height - _dotPosition.dy).toStringAsFixed(1)}',
239 1 : style: _fontStyle)),
240 1 : Positioned(
241 2 : left: _dotPosition.dx,
242 : top: 0,
243 1 : child: Container(
244 : width: 1,
245 2 : height: _windowSize.height,
246 : color: const Color(0xffff0000),
247 : )),
248 1 : Positioned(
249 : left: 0,
250 2 : top: _dotPosition.dy,
251 1 : child: Container(
252 2 : width: _windowSize.width,
253 : height: 1,
254 : color: const Color(0xffff0000),
255 : )),
256 1 : Positioned(
257 5 : left: _dotPosition.dx - _dotOffset.dx,
258 5 : top: _dotPosition.dy - _dotOffset.dy,
259 1 : child: GestureDetector(
260 1 : onPanUpdate: _onPanUpdate,
261 1 : onPanEnd: _onPanEnd,
262 1 : child: Container(
263 1 : child: Center(
264 1 : child: Container(
265 3 : height: _dotSize.width / 2.5,
266 3 : width: _dotSize.height / 2.5,
267 1 : decoration: BoxDecoration(
268 : shape: BoxShape.circle,
269 1 : color: Colors.red.withOpacity(0.8)),
270 : ),
271 : ),
272 2 : height: _dotSize.height,
273 2 : width: _dotSize.width,
274 1 : decoration: BoxDecoration(
275 1 : borderRadius: _radius,
276 1 : border: Border.all(color: Colors.black, width: 2)),
277 : ),
278 : ),
279 : ),
280 1 : Positioned(
281 : left: 16,
282 1 : top: _toolBarY,
283 1 : child: GestureDetector(
284 1 : onVerticalDragUpdate: _toolBarPanUpdate, child: toolBar)),
285 1 : InspectorOverlay(
286 1 : selection: _selection, needDescription: false, needEdges: false),
287 : ],
288 : ),
289 : );
290 : }
291 : }
|