mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 23:24:32 +00:00
102 lines
3.1 KiB
Swift
102 lines
3.1 KiB
Swift
/*
|
|
* Atoll (DynamicIsland)
|
|
* Copyright (C) 2024-2026 Atoll Contributors
|
|
*
|
|
* Originally from boring.notch project
|
|
* Modified and adapted for Atoll (DynamicIsland)
|
|
* See NOTICE for details.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import Foundation
|
|
import Combine
|
|
|
|
private let _shelfTypeAnchor: Bool = {
|
|
_ = String(describing: ShelfItem.self)
|
|
return true
|
|
}()
|
|
|
|
@MainActor
|
|
final class ShelfSelectionModel: ObservableObject {
|
|
static let shared = ShelfSelectionModel()
|
|
|
|
@Published private(set) var selectedIDs: Set<UUID> = []
|
|
|
|
// Anchor for shift-range selection
|
|
private var lastAnchorID: UUID? = nil
|
|
|
|
func isSelected(_ id: UUID) -> Bool { selectedIDs.contains(id) }
|
|
|
|
var hasSelection: Bool { !selectedIDs.isEmpty }
|
|
|
|
var firstSelectedItem: ShelfItem? {
|
|
guard let firstID = selectedIDs.first else { return nil }
|
|
return ShelfStateViewModel.shared.items.first(where: { $0.id == firstID })
|
|
}
|
|
|
|
func selectedItems(in allItems: [ShelfItem]) -> [ShelfItem] {
|
|
allItems.filter { selectedIDs.contains($0.id) }
|
|
}
|
|
|
|
func selectSingle(_ item: ShelfItem) {
|
|
selectedIDs = [item.id]
|
|
lastAnchorID = item.id
|
|
}
|
|
|
|
func toggle(_ item: ShelfItem) {
|
|
if selectedIDs.contains(item.id) {
|
|
selectedIDs.remove(item.id)
|
|
} else {
|
|
selectedIDs.insert(item.id)
|
|
}
|
|
lastAnchorID = item.id
|
|
}
|
|
|
|
func shiftSelect(to item: ShelfItem, in allItems: [ShelfItem]) {
|
|
// Determine anchor
|
|
let anchorID = lastAnchorID ?? selectedIDs.first ?? item.id
|
|
guard let startIndex = allItems.firstIndex(where: { $0.id == anchorID }),
|
|
let endIndex = allItems.firstIndex(where: { $0.id == item.id }) else {
|
|
// Fallback to single select if indices not found
|
|
return selectSingle(item)
|
|
}
|
|
let lower = min(startIndex, endIndex)
|
|
let upper = max(startIndex, endIndex)
|
|
let rangeIDs = allItems[lower...upper].map { $0.id }
|
|
selectedIDs = Set(rangeIDs)
|
|
}
|
|
|
|
func clear() {
|
|
selectedIDs.removeAll()
|
|
lastAnchorID = nil
|
|
}
|
|
|
|
// Keep anchor sane if items array changed drastically (optional helper)
|
|
func ensureValidAnchor(in allItems: [ShelfItem]) {
|
|
if let anchor = lastAnchorID, !allItems.contains(where: { $0.id == anchor }) {
|
|
lastAnchorID = selectedIDs.first
|
|
}
|
|
}
|
|
|
|
@Published private(set) var isDragging: Bool = false
|
|
|
|
func beginDrag() {
|
|
isDragging = true
|
|
}
|
|
|
|
func endDrag() {
|
|
isDragging = false
|
|
}
|
|
}
|