Skip to content

Commit 5f554a9

Browse files
committed
Update main.dart
1 parent c256c04 commit 5f554a9

1 file changed

Lines changed: 154 additions & 23 deletions

File tree

examples/window_example/lib/main.dart

Lines changed: 154 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ class _WindowManagerPageState extends State<WindowManagerPage> {
369369
}
370370
}
371371

372-
class WindowCanvas extends StatelessWidget {
372+
class WindowCanvas extends StatefulWidget {
373373
final List<Window> windows;
374374
final List<Display> displays;
375375
final Window? selectedWindow;
@@ -383,9 +383,60 @@ class WindowCanvas extends StatelessWidget {
383383
required this.onWindowTap,
384384
});
385385

386+
@override
387+
State<WindowCanvas> createState() => _WindowCanvasState();
388+
}
389+
390+
class _WindowCanvasState extends State<WindowCanvas> {
391+
final TransformationController _transformationController =
392+
TransformationController();
393+
double _baseScale = 1.0;
394+
395+
@override
396+
void initState() {
397+
super.initState();
398+
_transformationController.addListener(_onTransformationChanged);
399+
}
400+
401+
@override
402+
void dispose() {
403+
_transformationController.removeListener(_onTransformationChanged);
404+
_transformationController.dispose();
405+
super.dispose();
406+
}
407+
408+
void _onTransformationChanged() {
409+
final currentScale = _transformationController.value.getMaxScaleOnAxis();
410+
if (currentScale != _baseScale) {
411+
setState(() {
412+
_baseScale = currentScale;
413+
});
414+
}
415+
}
416+
417+
void _zoomIn() {
418+
final currentScale = _transformationController.value.getMaxScaleOnAxis();
419+
final newScale = (currentScale * 1.2).clamp(0.5, 5.0);
420+
_transformationController.value = Matrix4.identity().scaled(newScale);
421+
}
422+
423+
void _zoomOut() {
424+
final currentScale = _transformationController.value.getMaxScaleOnAxis();
425+
final newScale = (currentScale / 1.2).clamp(0.5, 5.0);
426+
_transformationController.value = Matrix4.identity().scaled(newScale);
427+
}
428+
429+
void _resetZoom() {
430+
_transformationController.value = Matrix4.identity();
431+
}
432+
433+
void _fitToScreen() {
434+
_resetZoom();
435+
}
436+
386437
@override
387438
Widget build(BuildContext context) {
388-
if (windows.isEmpty && displays.isEmpty) {
439+
if (widget.windows.isEmpty && widget.displays.isEmpty) {
389440
return const Center(child: Text('No windows or displays available'));
390441
}
391442

@@ -401,9 +452,87 @@ class WindowCanvas extends StatelessWidget {
401452
),
402453
borderRadius: BorderRadius.circular(12),
403454
),
404-
padding: const EdgeInsets.all(20),
405-
child: LayoutBuilder(
406-
builder: (context, constraints) => _buildWindowLayout(constraints),
455+
child: Stack(
456+
children: [
457+
// Main canvas with zoom support
458+
Padding(
459+
padding: const EdgeInsets.all(20),
460+
child: LayoutBuilder(
461+
builder: (context, constraints) => InteractiveViewer(
462+
transformationController: _transformationController,
463+
minScale: 0.5,
464+
maxScale: 5.0,
465+
boundaryMargin: const EdgeInsets.all(20),
466+
panEnabled: true,
467+
scaleEnabled: true,
468+
child: _buildWindowLayout(constraints),
469+
),
470+
),
471+
),
472+
// Zoom controls
473+
Positioned(
474+
top: 8,
475+
right: 8,
476+
child: Container(
477+
decoration: BoxDecoration(
478+
color: Colors.white.withOpacity(0.9),
479+
borderRadius: BorderRadius.circular(8),
480+
boxShadow: [
481+
BoxShadow(
482+
color: Colors.black.withOpacity(0.1),
483+
blurRadius: 4,
484+
offset: const Offset(0, 2),
485+
),
486+
],
487+
),
488+
child: Column(
489+
mainAxisSize: MainAxisSize.min,
490+
children: [
491+
IconButton(
492+
icon: const Icon(Icons.add),
493+
onPressed: _zoomIn,
494+
tooltip: 'Zoom In',
495+
iconSize: 20,
496+
),
497+
const Divider(height: 1),
498+
IconButton(
499+
icon: const Icon(Icons.remove),
500+
onPressed: _zoomOut,
501+
tooltip: 'Zoom Out',
502+
iconSize: 20,
503+
),
504+
const Divider(height: 1),
505+
IconButton(
506+
icon: const Icon(Icons.fit_screen),
507+
onPressed: _fitToScreen,
508+
tooltip: 'Fit to Screen',
509+
iconSize: 20,
510+
),
511+
],
512+
),
513+
),
514+
),
515+
// Scale indicator
516+
Positioned(
517+
bottom: 8,
518+
left: 8,
519+
child: Container(
520+
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
521+
decoration: BoxDecoration(
522+
color: Colors.black.withOpacity(0.6),
523+
borderRadius: BorderRadius.circular(4),
524+
),
525+
child: Text(
526+
'${(_baseScale * 100).toStringAsFixed(0)}%',
527+
style: const TextStyle(
528+
color: Colors.white,
529+
fontSize: 12,
530+
fontWeight: FontWeight.bold,
531+
),
532+
),
533+
),
534+
),
535+
],
407536
),
408537
),
409538
);
@@ -422,19 +551,21 @@ class WindowCanvas extends StatelessWidget {
422551
final scaleY = constraints.maxHeight / bounds.height;
423552
final scale = (scaleX < scaleY ? scaleX : scaleY) * 0.85;
424553

425-
return Center(
426-
child: SizedBox(
427-
width: bounds.width * scale,
428-
height: bounds.height * scale,
429-
child: Stack(
430-
children: [
431-
// Draw displays first as background
432-
if (displays.isNotEmpty)
433-
...displays.map((display) => _buildDisplay(display, bounds, scale)).toList(),
434-
// Draw windows on top
435-
...windows.map((window) => _buildWindow(window, bounds, scale)).toList(),
436-
],
437-
),
554+
return SizedBox(
555+
width: bounds.width * scale,
556+
height: bounds.height * scale,
557+
child: Stack(
558+
children: [
559+
// Draw displays first as background
560+
if (widget.displays.isNotEmpty)
561+
...widget.displays
562+
.map((display) => _buildDisplay(display, bounds, scale))
563+
.toList(),
564+
// Draw windows on top
565+
...widget.windows
566+
.map((window) => _buildWindow(window, bounds, scale))
567+
.toList(),
568+
],
438569
),
439570
);
440571
}
@@ -446,8 +577,8 @@ class WindowCanvas extends StatelessWidget {
446577
double maxY = double.negativeInfinity;
447578

448579
// First, include all displays if available
449-
if (displays.isNotEmpty) {
450-
for (final display in displays) {
580+
if (widget.displays.isNotEmpty) {
581+
for (final display in widget.displays) {
451582
final position = display.position;
452583
final size = display.size;
453584
minX = minX < position.dx ? minX : position.dx;
@@ -462,7 +593,7 @@ class WindowCanvas extends StatelessWidget {
462593
}
463594

464595
// Then, include all windows
465-
for (final window in windows) {
596+
for (final window in widget.windows) {
466597
try {
467598
final windowBounds = window.bounds;
468599
minX = minX < windowBounds.left ? minX : windowBounds.left;
@@ -585,7 +716,7 @@ class WindowCanvas extends StatelessWidget {
585716
final contentWidth = contentBounds.width * scale;
586717
final contentHeight = contentBounds.height * scale;
587718

588-
final isSelected = selectedWindow?.id == window.id;
719+
final isSelected = widget.selectedWindow?.id == window.id;
589720

590721
// Only draw if window is visible within bounds
591722
if (windowLeft + windowWidth < 0 ||
@@ -599,7 +730,7 @@ class WindowCanvas extends StatelessWidget {
599730
left: windowLeft,
600731
top: windowTop,
601732
child: GestureDetector(
602-
onTap: () => onWindowTap(window),
733+
onTap: () => widget.onWindowTap(window),
603734
child: AnimatedContainer(
604735
duration: const Duration(milliseconds: 200),
605736
width: windowWidth,

0 commit comments

Comments
 (0)