1+ import 'dart:async' ;
12import 'package:flutter/material.dart' ;
23import 'package:nativeapi/nativeapi.dart' ;
34
@@ -34,11 +35,86 @@ class _DisplayManagerPageState extends State<DisplayManagerPage> {
3435 Display ? _selectedDisplay;
3536 bool _isLoading = true ;
3637 String ? _errorMessage;
38+ Window ? _currentWindow;
39+ Offset _cursorPosition = Offset .zero;
40+ Timer ? _updateTimer;
41+ List <int > _windowListenerIds = [];
3742
3843 @override
3944 void initState () {
4045 super .initState ();
4146 _loadDisplays ();
47+ _startTracking ();
48+ }
49+
50+ @override
51+ void dispose () {
52+ _updateTimer? .cancel ();
53+ final windowManager = WindowManager .instance;
54+ for (final listenerId in _windowListenerIds) {
55+ windowManager.removeListener (listenerId);
56+ }
57+ _windowListenerIds.clear ();
58+ super .dispose ();
59+ }
60+
61+ void _startTracking () {
62+ // Update cursor position and current window periodically
63+ _updateTimer = Timer .periodic (const Duration (milliseconds: 100 ), (timer) {
64+ if (mounted) {
65+ _updateCursorAndWindow ();
66+ }
67+ });
68+
69+ // Listen to window events to update current window
70+ final windowManager = WindowManager .instance;
71+ _windowListenerIds.add (
72+ windowManager.addCallbackListener <WindowFocusedEvent >((event) {
73+ if (mounted) {
74+ _updateCurrentWindow ();
75+ }
76+ }),
77+ );
78+ _windowListenerIds.add (
79+ windowManager.addCallbackListener <WindowMovedEvent >((event) {
80+ if (mounted) {
81+ _updateCurrentWindow ();
82+ }
83+ }),
84+ );
85+ _windowListenerIds.add (
86+ windowManager.addCallbackListener <WindowResizedEvent >((event) {
87+ if (mounted) {
88+ _updateCurrentWindow ();
89+ }
90+ }),
91+ );
92+ }
93+
94+ void _updateCursorAndWindow () {
95+ final displayManager = DisplayManager .instance;
96+ final cursorPos = displayManager.getCursorPosition ();
97+
98+ final windowManager = WindowManager .instance;
99+ final currentWindow = windowManager.getCurrent ();
100+
101+ if (mounted) {
102+ setState (() {
103+ _cursorPosition = cursorPos;
104+ _currentWindow = currentWindow;
105+ });
106+ }
107+ }
108+
109+ void _updateCurrentWindow () {
110+ final windowManager = WindowManager .instance;
111+ final currentWindow = windowManager.getCurrent ();
112+
113+ if (mounted) {
114+ setState (() {
115+ _currentWindow = currentWindow;
116+ });
117+ }
42118 }
43119
44120 Future <void > _loadDisplays () async {
@@ -196,6 +272,8 @@ class _DisplayManagerPageState extends State<DisplayManagerPage> {
196272 displays: _displays,
197273 selectedDisplay: _selectedDisplay,
198274 onDisplayTap: _selectDisplay,
275+ currentWindow: _currentWindow,
276+ cursorPosition: _cursorPosition,
199277 ),
200278 ),
201279 if (_selectedDisplay != null )
@@ -214,6 +292,8 @@ class _DisplayManagerPageState extends State<DisplayManagerPage> {
214292 displays: _displays,
215293 selectedDisplay: _selectedDisplay,
216294 onDisplayTap: _selectDisplay,
295+ currentWindow: _currentWindow,
296+ cursorPosition: _cursorPosition,
217297 ),
218298 ),
219299 if (_selectedDisplay != null )
@@ -233,12 +313,16 @@ class DisplayCanvas extends StatelessWidget {
233313 final List <Display > displays;
234314 final Display ? selectedDisplay;
235315 final Function (Display ) onDisplayTap;
316+ final Window ? currentWindow;
317+ final Offset cursorPosition;
236318
237319 const DisplayCanvas ({
238320 super .key,
239321 required this .displays,
240322 required this .selectedDisplay,
241323 required this .onDisplayTap,
324+ this .currentWindow,
325+ this .cursorPosition = Offset .zero,
242326 });
243327
244328 @override
@@ -281,9 +365,14 @@ class DisplayCanvas extends StatelessWidget {
281365 width: bounds.width * scale,
282366 height: bounds.height * scale,
283367 child: Stack (
284- children: displays
285- .map ((display) => _buildDisplay (display, bounds, scale))
286- .toList (),
368+ children: [
369+ ...displays
370+ .map ((display) => _buildDisplay (display, bounds, scale))
371+ .toList (),
372+ if (currentWindow != null )
373+ _buildWindow (currentWindow! , bounds, scale),
374+ _buildCursor (bounds, scale),
375+ ],
287376 ),
288377 ),
289378 );
@@ -467,12 +556,6 @@ class DisplayCanvas extends StatelessWidget {
467556 return Colors .grey[200 ]! ;
468557 }
469558
470- Color _getDisplayBorderColor (bool isSelected, bool isPrimary) {
471- if (isSelected) return Colors .blue[600 ]! ;
472- if (isPrimary) return Colors .green[600 ]! ;
473- return Colors .grey[400 ]! ;
474- }
475-
476559 List <Color > _getWorkAreaGradient (bool isSelected, bool isPrimary) {
477560 if (isSelected) {
478561 return [Colors .blue[400 ]! , Colors .blue[600 ]! ];
@@ -482,6 +565,132 @@ class DisplayCanvas extends StatelessWidget {
482565 }
483566 return [Colors .grey[300 ]! , Colors .grey[400 ]! ];
484567 }
568+
569+ Widget _buildWindow (Window window, Rect bounds, double scale) {
570+ try {
571+ final windowBounds = window.bounds;
572+ final windowLeft = (windowBounds.left - bounds.left) * scale;
573+ final windowTop = (windowBounds.top - bounds.top) * scale;
574+ final windowWidth = windowBounds.width * scale;
575+ final windowHeight = windowBounds.height * scale;
576+
577+ // Only draw if window is visible within bounds
578+ if (windowLeft + windowWidth < 0 ||
579+ windowTop + windowHeight < 0 ||
580+ windowLeft > bounds.width * scale ||
581+ windowTop > bounds.height * scale) {
582+ return const SizedBox .shrink ();
583+ }
584+
585+ return Positioned (
586+ left: windowLeft,
587+ top: windowTop,
588+ child: Container (
589+ width: windowWidth,
590+ height: windowHeight,
591+ decoration: BoxDecoration (
592+ border: Border .all (
593+ color: Colors .orange,
594+ width: 2 ,
595+ ),
596+ boxShadow: [
597+ BoxShadow (
598+ color: Colors .orange.withOpacity (0.3 ),
599+ blurRadius: 4 ,
600+ offset: const Offset (0 , 2 ),
601+ ),
602+ ],
603+ ),
604+ child: Stack (
605+ children: [
606+ // Window background (semi-transparent)
607+ Container (
608+ color: Colors .orange.withOpacity (0.1 ),
609+ ),
610+ // Window title bar indicator
611+ Container (
612+ height: (20 * scale).clamp (8.0 , 20.0 ),
613+ decoration: BoxDecoration (
614+ color: Colors .orange.withOpacity (0.3 ),
615+ border: const Border (
616+ bottom: BorderSide (color: Colors .orange, width: 1 ),
617+ ),
618+ ),
619+ padding: EdgeInsets .symmetric (
620+ horizontal: (8 * scale).clamp (4.0 , 8.0 ),
621+ ),
622+ child: Row (
623+ children: [
624+ Icon (
625+ Icons .window,
626+ size: (12 * scale).clamp (8.0 , 12.0 ),
627+ color: Colors .orange[900 ],
628+ ),
629+ SizedBox (width: (4 * scale).clamp (2.0 , 4.0 )),
630+ Expanded (
631+ child: Text (
632+ window.title.isNotEmpty ? window.title : 'Window' ,
633+ style: TextStyle (
634+ fontSize: (10 * scale).clamp (6.0 , 10.0 ),
635+ color: Colors .orange[900 ],
636+ fontWeight: FontWeight .bold,
637+ overflow: TextOverflow .ellipsis,
638+ ),
639+ ),
640+ ),
641+ ],
642+ ),
643+ ),
644+ ],
645+ ),
646+ ),
647+ );
648+ } catch (e) {
649+ // Window might have been destroyed, return empty widget
650+ return const SizedBox .shrink ();
651+ }
652+ }
653+
654+ Widget _buildCursor (Rect bounds, double scale) {
655+ final cursorLeft = (cursorPosition.dx - bounds.left) * scale;
656+ final cursorTop = (cursorPosition.dy - bounds.top) * scale;
657+
658+ // Only draw if cursor is within bounds
659+ if (cursorLeft < 0 ||
660+ cursorTop < 0 ||
661+ cursorLeft > bounds.width * scale ||
662+ cursorTop > bounds.height * scale) {
663+ return const SizedBox .shrink ();
664+ }
665+
666+ return Positioned (
667+ left: cursorLeft - 8 ,
668+ top: cursorTop - 8 ,
669+ child: Container (
670+ width: 16 ,
671+ height: 16 ,
672+ decoration: BoxDecoration (
673+ color: Colors .red.withOpacity (0.8 ),
674+ shape: BoxShape .circle,
675+ border: Border .all (color: Colors .white, width: 2 ),
676+ boxShadow: [
677+ BoxShadow (
678+ color: Colors .red.withOpacity (0.5 ),
679+ blurRadius: 4 ,
680+ spreadRadius: 2 ,
681+ ),
682+ ],
683+ ),
684+ child: const Center (
685+ child: Icon (
686+ Icons .mouse,
687+ size: 8 ,
688+ color: Colors .white,
689+ ),
690+ ),
691+ ),
692+ );
693+ }
485694}
486695
487696class DisplayDetails extends StatelessWidget {
0 commit comments