4
4
using CommunityToolkit . WinUI . UI ;
5
5
using Microsoft . UI . Xaml ;
6
6
using Microsoft . UI . Xaml . Controls ;
7
+ using Microsoft . UI . Xaml . Input ;
7
8
using Microsoft . UI . Xaml . Shapes ;
8
9
using Windows . ApplicationModel . DataTransfer ;
9
10
using Windows . Storage ;
@@ -62,6 +63,12 @@ public Rectangle DragArea
62
63
/// <summary> Starting time when dragging a tab. </summary>
63
64
private DateTimeOffset dragStartTime ;
64
65
66
+ /// <summary>
67
+ /// Indicates if drag operation should be canceled.
68
+ /// This value gets reset at the start of the drag operation
69
+ /// </summary>
70
+ private bool isCancelingDragOperation ;
71
+
65
72
public TabBar ( )
66
73
{
67
74
InitializeComponent ( ) ;
@@ -82,9 +89,7 @@ public TabBar()
82
89
private void TabView_TabItemsChanged ( TabView sender , Windows . Foundation . Collections . IVectorChangedEventArgs args )
83
90
{
84
91
if ( args . CollectionChange == Windows . Foundation . Collections . CollectionChange . ItemRemoved )
85
- {
86
92
App . AppModel . TabStripSelectedIndex = Items . IndexOf ( HorizontalTabView . SelectedItem as TabBarItem ) ;
87
- }
88
93
89
94
if ( App . AppModel . TabStripSelectedIndex >= 0 && App . AppModel . TabStripSelectedIndex < Items . Count )
90
95
{
@@ -132,19 +137,34 @@ private void TabHoverSelected(object sender, object e)
132
137
{
133
138
tabHoverTimer . Stop ( ) ;
134
139
if ( hoveredTabViewItem is not null )
135
- {
136
140
App . AppModel . TabStripSelectedIndex = Items . IndexOf ( hoveredTabViewItem . DataContext as TabBarItem ) ;
137
- }
138
141
}
139
142
140
143
private void TabView_TabDragStarting ( TabView sender , TabViewTabDragStartingEventArgs args )
141
144
{
145
+ // Reset value
146
+ isCancelingDragOperation = false ;
147
+
142
148
var tabViewItemArgs = ( args . Item as TabBarItem ) . NavigationParameter ;
143
149
args . Data . Properties . Add ( TabPathIdentifier , tabViewItemArgs . Serialize ( ) ) ;
144
150
args . Data . RequestedOperation = DataPackageOperation . Move ;
145
151
152
+ // Get cursor position & time to track how far the tab was dragged.
146
153
InteropHelpers . GetCursorPos ( out dragStartPoint ) ;
147
154
dragStartTime = DateTimeOffset . UtcNow ;
155
+
156
+ // Focus the UI Element, without this the focus sometimes changes
157
+ // and the PreviewKeyDown event won't trigger.
158
+ Focus ( FocusState . Programmatic ) ;
159
+ PreviewKeyDown += TabDragging_PreviewKeyDown ;
160
+ }
161
+
162
+ private void TabDragging_PreviewKeyDown ( object sender , KeyRoutedEventArgs e )
163
+ {
164
+ // Pressing escape will automatically complete the drag event but we need to set the
165
+ // isCancelingDragOperation field in order to detect if escape was pressed.
166
+ if ( e . Key is Windows . System . VirtualKey . Escape )
167
+ isCancelingDragOperation = true ;
148
168
}
149
169
150
170
private void TabView_TabStripDragOver ( object sender , DragEventArgs e )
@@ -178,9 +198,7 @@ private async void TabView_TabStripDrop(object sender, DragEventArgs e)
178
198
179
199
if ( ! e . DataView . Properties . TryGetValue ( TabPathIdentifier , out object tabViewItemPathObj ) ||
180
200
! ( tabViewItemPathObj is string tabViewItemString ) )
181
- {
182
201
return ;
183
- }
184
202
185
203
var index = - 1 ;
186
204
@@ -202,24 +220,27 @@ private async void TabView_TabStripDrop(object sender, DragEventArgs e)
202
220
203
221
private void TabView_TabDragCompleted ( TabView sender , TabViewTabDragCompletedEventArgs args )
204
222
{
223
+ // Unsubscribe from the key down event, it's only needed when a tab is actively being dragged
224
+ PreviewKeyDown -= TabDragging_PreviewKeyDown ;
225
+
205
226
if ( ApplicationData . Current . LocalSettings . Values . ContainsKey ( TabDropHandledIdentifier ) &&
206
227
( bool ) ApplicationData . Current . LocalSettings . Values [ TabDropHandledIdentifier ] )
207
- {
208
228
CloseTab ( args . Item as TabBarItem ) ;
209
- }
210
229
else
211
- {
212
230
HorizontalTabView . SelectedItem = args . Tab ;
213
- }
214
231
215
232
if ( ApplicationData . Current . LocalSettings . Values . ContainsKey ( TabDropHandledIdentifier ) )
216
- {
217
233
ApplicationData . Current . LocalSettings . Values . Remove ( TabDropHandledIdentifier ) ;
218
- }
219
234
}
220
235
221
236
private async void TabView_TabDroppedOutside ( TabView sender , TabViewTabDroppedOutsideEventArgs args )
222
237
{
238
+ // Unsubscribe from the key down event, it's only needed when a tab is actively being dragged
239
+ PreviewKeyDown -= TabDragging_PreviewKeyDown ;
240
+
241
+ if ( isCancelingDragOperation )
242
+ return ;
243
+
223
244
InteropHelpers . GetCursorPos ( out var droppedPoint ) ;
224
245
var droppedTime = DateTimeOffset . UtcNow ;
225
246
var dragTime = droppedTime - dragStartTime ;
@@ -241,10 +262,8 @@ private async void TabView_TabDroppedOutside(TabView sender, TabViewTabDroppedOu
241
262
sender . SelectedIndex = selectedTabViewItemIndex ;
242
263
}
243
264
else
244
- {
245
265
// Dispose tab arguments
246
266
( args . Item as TabBarItem ) ? . Unload ( ) ;
247
- }
248
267
}
249
268
250
269
private void TabItemContextMenu_Opening ( object sender , object e )
0 commit comments