@@ -37,9 +37,7 @@ public MainWindow()
3737 _viewModel = new MainViewModel ( ) ;
3838 this . DataContext = _viewModel ;
3939
40- // Position window at the top center of the screen
41- this . Left = ( SystemParameters . PrimaryScreenWidth - this . Width ) / 2 ;
42- this . Top = 0 ;
40+ ApplyWindowPosition ( ) ;
4341
4442 // Initialize visual state to Idle
4543 _viewModel . CurrentState = IslandState . Idle ;
@@ -64,6 +62,82 @@ protected override void OnContentRendered(EventArgs e)
6462 }
6563 }
6664
65+ private void ApplyWindowPosition ( )
66+ {
67+ if ( _viewModel . HasCustomPosition )
68+ {
69+ this . Left = _viewModel . WindowLeft ;
70+ var deviceTopLeft = DipsToDevice ( new Point ( _viewModel . WindowLeft , _viewModel . WindowTop ) ) ;
71+ var deviceSize = DipsToDevice ( new Point ( this . Width , this . Height ) ) ;
72+ var rect = new System . Drawing . Rectangle (
73+ ( int ) deviceTopLeft . X ,
74+ ( int ) deviceTopLeft . Y ,
75+ ( int ) deviceSize . X ,
76+ ( int ) deviceSize . Y ) ;
77+ var screen = System . Windows . Forms . Screen . FromRectangle ( rect ) ;
78+ this . Top = GetScreenBoundsInDips ( screen ) . Top ;
79+ }
80+ else
81+ {
82+ var screen = System . Windows . Forms . Screen . PrimaryScreen ;
83+ if ( screen != null )
84+ {
85+ var bounds = GetScreenBoundsInDips ( screen ) ;
86+ this . Left = bounds . Left + ( bounds . Width - this . Width ) / 2 ;
87+ this . Top = bounds . Top ;
88+ }
89+ }
90+ }
91+
92+ private Point DipsToDevice ( Point point )
93+ {
94+ var dpi = VisualTreeHelper . GetDpi ( this ) ;
95+ return new Point ( point . X * dpi . DpiScaleX , point . Y * dpi . DpiScaleY ) ;
96+ }
97+
98+ private Point DeviceToDips ( Point point )
99+ {
100+ var dpi = VisualTreeHelper . GetDpi ( this ) ;
101+ return new Point ( point . X / dpi . DpiScaleX , point . Y / dpi . DpiScaleY ) ;
102+ }
103+
104+ private Rect GetScreenBoundsInDips ( System . Windows . Forms . Screen screen )
105+ {
106+ var topLeft = DeviceToDips ( new Point ( screen . Bounds . Left , screen . Bounds . Top ) ) ;
107+ var bottomRight = DeviceToDips ( new Point ( screen . Bounds . Right , screen . Bounds . Bottom ) ) ;
108+ return new Rect ( topLeft , bottomRight ) ;
109+ }
110+
111+ private System . Windows . Forms . Screen GetScreenFromDips ( Point point )
112+ {
113+ var devicePoint = DipsToDevice ( point ) ;
114+ return System . Windows . Forms . Screen . FromPoint ( new System . Drawing . Point ( ( int ) devicePoint . X , ( int ) devicePoint . Y ) ) ;
115+ }
116+
117+ private Point GetMouseScreenPositionInDips ( MouseEventArgs e )
118+ {
119+ return DeviceToDips ( PointToScreen ( e . GetPosition ( this ) ) ) ;
120+ }
121+
122+ private void UpdateEditModeVisuals ( )
123+ {
124+ if ( _viewModel . IsPositionEditMode )
125+ {
126+ HudBorder . Background = new SolidColorBrush ( Color . FromArgb ( 0x40 , 0x00 , 0xFF , 0x00 ) ) ;
127+ HudBorder . Cursor = Cursors . SizeAll ;
128+ _viewModel . CurrentState = IslandState . Music ;
129+ }
130+ else
131+ {
132+ HudBorder . Background = new SolidColorBrush ( Color . FromArgb ( 0x01 , 0xFF , 0xFF , 0xFF ) ) ;
133+ HudBorder . Cursor = Cursors . Arrow ;
134+ if ( ! HudBorder . IsMouseOver )
135+ {
136+ _viewModel . CurrentState = IslandState . Idle ;
137+ }
138+ }
139+ }
140+
67141 private void InitializeSystemTray ( )
68142 {
69143 _notifyIcon = new NotifyIcon ( ) ;
@@ -137,6 +211,14 @@ private void ViewModel_PropertyChanged(object? sender, PropertyChangedEventArgs
137211 {
138212 Dispatcher . Invoke ( UpdateLocalizedShellText ) ;
139213 }
214+ else if ( e . PropertyName == nameof ( MainViewModel . HasCustomPosition ) )
215+ {
216+ Dispatcher . Invoke ( ApplyWindowPosition ) ;
217+ }
218+ else if ( e . PropertyName == nameof ( MainViewModel . IsPositionEditMode ) )
219+ {
220+ Dispatcher . Invoke ( UpdateEditModeVisuals ) ;
221+ }
140222 }
141223
142224 private void UpdateLocalizedShellText ( )
@@ -243,16 +325,61 @@ private void AnimateSize(double targetWidth, double targetHeight, bool isClosing
243325
244326 private void HudBorder_MouseEnter ( object sender , MouseEventArgs e )
245327 {
246- if ( _isClosingApp ) return ;
328+ if ( _isClosingApp || _viewModel . IsPositionEditMode ) return ;
247329 _viewModel . CurrentState = IslandState . Music ;
248330 }
249331
250332 private void HudBorder_MouseLeave ( object sender , MouseEventArgs e )
251333 {
252- if ( _isClosingApp ) return ;
334+ if ( _isClosingApp || _viewModel . IsPositionEditMode ) return ;
253335 _viewModel . CurrentState = IslandState . Idle ;
254336 }
255337
338+ private bool _isDragging = false ;
339+ private double _dragStartLeft ;
340+ private double _dragStartMouseX ;
341+
342+ private void HudBorder_MouseLeftButtonDown ( object sender , MouseButtonEventArgs e )
343+ {
344+ if ( _viewModel . IsPositionEditMode && e . LeftButton == MouseButtonState . Pressed )
345+ {
346+ _isDragging = true ;
347+ _dragStartLeft = this . Left ;
348+ _dragStartMouseX = GetMouseScreenPositionInDips ( e ) . X ;
349+ HudBorder . CaptureMouse ( ) ;
350+ }
351+ }
352+
353+ private void HudBorder_MouseMove ( object sender , MouseEventArgs e )
354+ {
355+ if ( _isDragging && _viewModel . IsPositionEditMode )
356+ {
357+ var currentMousePosition = GetMouseScreenPositionInDips ( e ) ;
358+ var currentMouseX = currentMousePosition . X ;
359+ var diffX = currentMouseX - _dragStartMouseX ;
360+ this . Left = _dragStartLeft + diffX ;
361+
362+ var screen = GetScreenFromDips ( currentMousePosition ) ;
363+ this . Top = GetScreenBoundsInDips ( screen ) . Top ;
364+ }
365+ }
366+
367+ private void HudBorder_MouseLeftButtonUp ( object sender , MouseButtonEventArgs e )
368+ {
369+ if ( _isDragging )
370+ {
371+ _isDragging = false ;
372+ HudBorder . ReleaseMouseCapture ( ) ;
373+
374+ var screen = GetScreenFromDips ( GetMouseScreenPositionInDips ( e ) ) ;
375+ this . Top = GetScreenBoundsInDips ( screen ) . Top ;
376+
377+ _viewModel . WindowLeft = this . Left ;
378+ _viewModel . WindowTop = this . Top ;
379+ _viewModel . HasCustomPosition = true ;
380+ }
381+ }
382+
256383 private void OpenSettings_Click ( object sender , RoutedEventArgs e )
257384 {
258385 OpenSettings ( ) ;
0 commit comments