Skip to content

Commit 15742b8

Browse files
author
Iakov Senatov
committed
.ondrag fixed
1 parent 3a83c99 commit 15742b8

File tree

3 files changed

+83
-31
lines changed

3 files changed

+83
-31
lines changed

GUI/Resources/curr_version.asc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2026.03.09 00:56:48 at Host: NEVA
1+
2026.03.09 22:45:55 at Host: NEVA

GUI/Sources/Features/Panels/ThumbnailGridView.swift

Lines changed: 80 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,47 @@ struct ThumbnailGridView: View {
2929
let onSelect: (CustomFile) -> Void
3030
let onDoubleClick: (CustomFile) -> Void
3131

32+
@State private var selectedIDs: Set<CustomFile.ID> = []
33+
3234
private var columns: [GridItem] {
3335
[GridItem(.adaptive(minimum: cellSize, maximum: cellSize + 20), spacing: 8)]
3436
}
3537

38+
private func handleSelection(for file: CustomFile, modifiers: NSEvent.ModifierFlags) {
39+
if modifiers.contains(.command) {
40+
if selectedIDs.contains(file.id) {
41+
selectedIDs.remove(file.id)
42+
} else {
43+
selectedIDs.insert(file.id)
44+
}
45+
46+
// Update anchor selection
47+
selectedID = selectedIDs.isEmpty ? nil : file.id
48+
49+
onSelect(file)
50+
return
51+
}
52+
53+
if modifiers.contains(.shift),
54+
let anchor = selectedID,
55+
let anchorIndex = files.firstIndex(where: { $0.id == anchor }),
56+
let targetIndex = files.firstIndex(where: { $0.id == file.id })
57+
{
58+
let lower = min(anchorIndex, targetIndex)
59+
let upper = max(anchorIndex, targetIndex)
60+
selectedIDs.removeAll()
61+
for item in files[lower...upper] {
62+
selectedIDs.insert(item.id)
63+
}
64+
selectedID = file.id
65+
onSelect(file)
66+
return
67+
}
68+
selectedIDs = [file.id]
69+
selectedID = file.id
70+
onSelect(file)
71+
}
72+
3673
// MARK: - Body
3774
var body: some View {
3875
ScrollView {
@@ -41,16 +78,19 @@ struct ThumbnailGridView: View {
4178
ThumbnailCellView(
4279
file: file,
4380
cellSize: cellSize,
44-
isSelected: selectedID == file.id,
81+
isSelected: selectedIDs.contains(file.id),
4582
panelSide: panelSide,
4683
dragFiles: dragFilesFor(file),
47-
onSelect: { onSelect(file) },
84+
onSelect: { modifiers in
85+
handleSelection(for: file, modifiers: modifiers)
86+
},
4887
onDoubleClick: { onDoubleClick(file) },
4988
onFileAction: { action in
5089
ContextMenuCoordinator.shared.handleFileAction(action, for: file, panel: panelSide, appState: appState)
5190
},
5291
onDirectoryAction: { action in
53-
ContextMenuCoordinator.shared.handleDirectoryAction(action, for: file, panel: panelSide, appState: appState)
92+
ContextMenuCoordinator.shared.handleDirectoryAction(
93+
action, for: file, panel: panelSide, appState: appState)
5494
}
5595
)
5696
}
@@ -61,8 +101,9 @@ struct ThumbnailGridView: View {
61101

62102
// MARK: - Drag helpers
63103
private func dragFilesFor(_ file: CustomFile) -> [CustomFile] {
64-
let marked = appState.markedCustomFiles(for: panelSide)
65-
if !marked.isEmpty && marked.contains(where: { $0.id == file.id }) { return marked }
104+
if selectedIDs.contains(file.id) {
105+
return files.filter { selectedIDs.contains($0.id) }
106+
}
66107
return [file]
67108
}
68109
}
@@ -76,7 +117,7 @@ private struct ThumbnailCellView: View {
76117
let isSelected: Bool
77118
let panelSide: PanelSide
78119
let dragFiles: [CustomFile]
79-
let onSelect: () -> Void
120+
let onSelect: (NSEvent.ModifierFlags) -> Void
80121
let onDoubleClick: () -> Void
81122
let onFileAction: (FileAction) -> Void
82123
let onDirectoryAction: (DirectoryAction) -> Void
@@ -95,9 +136,11 @@ private struct ThumbnailCellView: View {
95136
// Thumbnail or icon
96137
ZStack {
97138
RoundedRectangle(cornerRadius: 6, style: .continuous)
98-
.fill(isSelected
99-
? Color.accentColor.opacity(0.18)
100-
: (isHovered ? Color.primary.opacity(0.06) : Color.clear))
139+
.fill(
140+
isSelected
141+
? Color.accentColor.opacity(0.18)
142+
: (isHovered ? Color.primary.opacity(0.06) : Color.clear)
143+
)
101144
.frame(width: cellSize, height: cellSize)
102145

103146
if let img = thumbnail {
@@ -141,7 +184,13 @@ private struct ThumbnailCellView: View {
141184
.contentShape(Rectangle())
142185
.onHover { isHovered = $0 }
143186
.onTapGesture(count: 2) { onDoubleClick() }
144-
.onTapGesture(count: 1) { onSelect() }
187+
.simultaneousGesture(
188+
TapGesture(count: 1)
189+
.onEnded {
190+
let modifiers = NSApp.currentEvent?.modifierFlags ?? []
191+
onSelect(modifiers)
192+
}
193+
)
145194
// MARK: Context menu
146195
.contextMenu { contextMenuContent }
147196
// MARK: Drag
@@ -161,7 +210,8 @@ private struct ThumbnailCellView: View {
161210
provider.registerObject(first.urlValue as NSURL, visibility: .all)
162211
}
163212

164-
let allDraggedPaths = dragFiles
213+
let allDraggedPaths =
214+
dragFiles
165215
.map { $0.urlValue.absoluteString }
166216
.joined(separator: "\n")
167217

@@ -201,24 +251,24 @@ private struct ThumbnailCellView: View {
201251
private func sfSymbol(for name: String) -> String {
202252
let ext = (name as NSString).pathExtension.lowercased()
203253
switch ext {
204-
case "jpg", "jpeg", "png", "gif", "webp", "heic", "heif", "bmp", "tiff":
205-
return "photo"
206-
case "mp4", "mov", "avi", "mkv", "m4v", "wmv":
207-
return "film"
208-
case "mp3", "aac", "flac", "wav", "m4a", "ogg":
209-
return "music.note"
210-
case "pdf":
211-
return "doc.richtext"
212-
case "zip", "tar", "gz", "7z", "rar", "bz2":
213-
return "archivebox"
214-
case "swift", "py", "js", "ts", "java", "kt", "cpp", "c", "h", "m", "rb", "go", "rs":
215-
return "chevron.left.forwardslash.chevron.right"
216-
case "txt", "md", "rtf":
217-
return "doc.text"
218-
case "app":
219-
return "app.badge"
220-
default:
221-
return "doc"
254+
case "jpg", "jpeg", "png", "gif", "webp", "heic", "heif", "bmp", "tiff":
255+
return "photo"
256+
case "mp4", "mov", "avi", "mkv", "m4v", "wmv":
257+
return "film"
258+
case "mp3", "aac", "flac", "wav", "m4a", "ogg":
259+
return "music.note"
260+
case "pdf":
261+
return "doc.richtext"
262+
case "zip", "tar", "gz", "7z", "rar", "bz2":
263+
return "archivebox"
264+
case "swift", "py", "js", "ts", "java", "kt", "cpp", "c", "h", "m", "rb", "go", "rs":
265+
return "chevron.left.forwardslash.chevron.right"
266+
case "txt", "md", "rtf":
267+
return "doc.text"
268+
case "app":
269+
return "app.badge"
270+
default:
271+
return "doc"
222272
}
223273
}
224274

@@ -229,7 +279,7 @@ private struct ThumbnailCellView: View {
229279
if file.isDirectory { return }
230280

231281
let url = file.urlValue
232-
let size = CGSize(width: imageSize * 2, height: imageSize * 2) // 2x for retina
282+
let size = CGSize(width: imageSize * 2, height: imageSize * 2) // 2x for retina
233283
let scale = NSScreen.main?.backingScaleFactor ?? 2.0
234284
let request = QLThumbnailGenerator.Request(
235285
fileAt: url,

Scripts/local_release.zsh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ set -e
88
# скрипт сразу падает при любой ошибке, вместо тихого развала где-нибудь на середине.
99
set -euo pipefail
1010

11+
/Users/senat/Develop/MiMiNavigator/Scripts/refreshVersionFile.zsh
12+
1113
PROJECT_DIR="/Users/senat/Develop/MiMiNavigator"
1214
DERIVED_DATA="/tmp/mimi_build"
1315
APP_NAME="MiMiNavigator.app"

0 commit comments

Comments
 (0)