Skip to content

Commit

Permalink
feat: drag and drop over file list (#24)
Browse files Browse the repository at this point in the history
* feat: drag and drop over file list

* chore: workflow, update xcode version
  • Loading branch information
clement2026 authored Sep 21, 2024
1 parent 0763779 commit 5e317d5
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 70 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
uses: actions/checkout@v4
- name: Set up Xcode
run: |
sudo xcode-select -s "/Applications/Xcode_16.1_beta.app"
sudo xcode-select -s "/Applications/Xcode_16.app"
xcodebuild -version
- name: Allow macro
run: |
Expand Down
30 changes: 19 additions & 11 deletions approf.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
EB62B58B2C5FEC1B00188A9D /* ActionButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B58A2C5FEC1B00188A9D /* ActionButtonView.swift */; };
EB62B58F2C5FED3800188A9D /* ShortcutsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B58E2C5FED3800188A9D /* ShortcutsView.swift */; };
EB62B5922C5FEEE800188A9D /* PerviewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB62B5902C5FEEE800188A9D /* PerviewData.swift */; };
EB6530F52C41C09800A66B7C /* DropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6530F42C41C09800A66B7C /* DropView.swift */; };
EB6530F52C41C09800A66B7C /* DropAndImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6530F42C41C09800A66B7C /* DropAndImportView.swift */; };
EB6530FB2C41CF8D00A66B7C /* WebIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB6530FA2C41CF8D00A66B7C /* WebIndicatorView.swift */; };
EB659BC52C3EE91400F3F84E /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB659BC42C3EE91400F3F84E /* Command.swift */; };
EB7ADFC62C439CE500130909 /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB7ADFC52C439CE500130909 /* WindowController.swift */; };
Expand Down Expand Up @@ -78,6 +78,8 @@
EBBC581C2C668A0F00A72918 /* b.pb in Resources */ = {isa = PBXBuildFile; fileRef = EBBC58162C668A0F00A72918 /* b.pb */; };
EBBC581D2C668A0F00A72918 /* a.pb.gz in Resources */ = {isa = PBXBuildFile; fileRef = EBBC58152C668A0F00A72918 /* a.pb.gz */; };
EBBC581F2C669A8100A72918 /* TestDropFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBC581E2C669A8100A72918 /* TestDropFiles.swift */; };
EBC7C85E2C9DD31B00B330D3 /* DropAndAppendFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC7C85D2C9DD31B00B330D3 /* DropAndAppendFeature.swift */; };
EBC7C8602C9DD32D00B330D3 /* DropAndAppendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC7C85F2C9DD32D00B330D3 /* DropAndAppendView.swift */; };
EBD20FBD2C42D06300EE88D7 /* LightsOff.metal in Sources */ = {isa = PBXBuildFile; fileRef = EBD20FBC2C42D06300EE88D7 /* LightsOff.metal */; };
EBD292B42C430F8000FFB285 /* ShortcutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBD292B32C430F8000FFB285 /* ShortcutView.swift */; };
EBD292BA2C430F9700FFB285 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = EBD292B92C430F9700FFB285 /* KeyboardShortcuts */; };
Expand All @@ -90,7 +92,7 @@
EBE8334F2C3C8BBD00FD8A73 /* Tooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBE8334E2C3C8BBD00FD8A73 /* Tooltip.swift */; };
EBE833532C3C8D9500FD8A73 /* Ripple.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBE833522C3C8D9500FD8A73 /* Ripple.swift */; };
EBE833542C3C8D9500FD8A73 /* Ripple.metal in Sources */ = {isa = PBXBuildFile; fileRef = EBE833512C3C8D9500FD8A73 /* Ripple.metal */; };
EBEAAACA2C467DD60026C328 /* DropFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEAAAC92C467DD60026C328 /* DropFeature.swift */; };
EBEAAACA2C467DD60026C328 /* DropAndImportFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEAAAC92C467DD60026C328 /* DropAndImportFeature.swift */; };
EBEC3ABB2C3D0F3C0016F878 /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEC3ABA2C3D0F3C0016F878 /* Toolbar.swift */; };
EBEF3C002C3BBECB0003477D /* Exts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEF3BF72C3BBECB0003477D /* Exts.swift */; };
EBEF3C0B2C3BBF2A0003477D /* Logging in Frameworks */ = {isa = PBXBuildFile; productRef = EBEF3C0A2C3BBF2A0003477D /* Logging */; };
Expand Down Expand Up @@ -150,7 +152,7 @@
EB62B58A2C5FEC1B00188A9D /* ActionButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtonView.swift; sourceTree = "<group>"; };
EB62B58E2C5FED3800188A9D /* ShortcutsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsView.swift; sourceTree = "<group>"; };
EB62B5902C5FEEE800188A9D /* PerviewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerviewData.swift; sourceTree = "<group>"; };
EB6530F42C41C09800A66B7C /* DropView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropView.swift; sourceTree = "<group>"; };
EB6530F42C41C09800A66B7C /* DropAndImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAndImportView.swift; sourceTree = "<group>"; };
EB6530FA2C41CF8D00A66B7C /* WebIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebIndicatorView.swift; sourceTree = "<group>"; };
EB659BC42C3EE91400F3F84E /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
EB6E4C072C45424B005C1225 /* ci_post_clone.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = ci_post_clone.sh; sourceTree = "<group>"; };
Expand Down Expand Up @@ -203,6 +205,8 @@
EBBC58192C668A0F00A72918 /* TestDropFilesLegacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDropFilesLegacy.swift; sourceTree = "<group>"; };
EBBC581E2C669A8100A72918 /* TestDropFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDropFiles.swift; sourceTree = "<group>"; };
EBBC58202C669B0500A72918 /* TestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = TestPlan.xctestplan; path = approfTests/TestPlan.xctestplan; sourceTree = "<group>"; };
EBC7C85D2C9DD31B00B330D3 /* DropAndAppendFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAndAppendFeature.swift; sourceTree = "<group>"; };
EBC7C85F2C9DD32D00B330D3 /* DropAndAppendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAndAppendView.swift; sourceTree = "<group>"; };
EBD20FBC2C42D06300EE88D7 /* LightsOff.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = LightsOff.metal; sourceTree = "<group>"; };
EBD292B32C430F8000FFB285 /* ShortcutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutView.swift; sourceTree = "<group>"; };
EBD292BB2C43116400FFB285 /* SettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingView.swift; sourceTree = "<group>"; };
Expand All @@ -213,7 +217,7 @@
EBE8334E2C3C8BBD00FD8A73 /* Tooltip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tooltip.swift; sourceTree = "<group>"; };
EBE833512C3C8D9500FD8A73 /* Ripple.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Ripple.metal; sourceTree = "<group>"; };
EBE833522C3C8D9500FD8A73 /* Ripple.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ripple.swift; sourceTree = "<group>"; };
EBEAAAC92C467DD60026C328 /* DropFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropFeature.swift; sourceTree = "<group>"; };
EBEAAAC92C467DD60026C328 /* DropAndImportFeature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropAndImportFeature.swift; sourceTree = "<group>"; };
EBEC3ABA2C3D0F3C0016F878 /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = "<group>"; };
EBEF3A1E2C3B255A0003477D /* approf.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = approf.app; sourceTree = BUILT_PRODUCTS_DIR; };
EBEF3A2F2C3B255B0003477D /* approfTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = approfTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -307,14 +311,16 @@
path = ci_scripts;
sourceTree = "<group>";
};
EB8190EE2C45852F007EDE72 /* ImportFeature */ = {
EB8190EE2C45852F007EDE72 /* DragNDrop */ = {
isa = PBXGroup;
children = (
EBEAAAC92C467DD60026C328 /* DropFeature.swift */,
EB6530F42C41C09800A66B7C /* DropView.swift */,
EBEAAAC92C467DD60026C328 /* DropAndImportFeature.swift */,
EB6530F42C41C09800A66B7C /* DropAndImportView.swift */,
EBC7C85D2C9DD31B00B330D3 /* DropAndAppendFeature.swift */,
EBC7C85F2C9DD32D00B330D3 /* DropAndAppendView.swift */,
EB62B5802C5FE9FD00188A9D /* ImportView.swift */,
);
path = ImportFeature;
path = DragNDrop;
sourceTree = "<group>";
};
EB9444172C42F6E50050F0D1 /* Preview Content */ = {
Expand All @@ -339,7 +345,7 @@
isa = PBXGroup;
children = (
EB13E2C12C499E5E00484479 /* Setting */,
EB8190EE2C45852F007EDE72 /* ImportFeature */,
EB8190EE2C45852F007EDE72 /* DragNDrop */,
EBB794852C410F0900D5AE1C /* AppFeature.swift */,
EB0A27302C413CE400AD34ED /* NavigaionView.swift */,
EB85AB812C3DBA0A0080200A /* DetailFeature.swift */,
Expand Down Expand Up @@ -666,6 +672,7 @@
files = (
EB0A27312C413CE400AD34ED /* NavigaionView.swift in Sources */,
EBD292B42C430F8000FFB285 /* ShortcutView.swift in Sources */,
EBC7C8602C9DD32D00B330D3 /* DropAndAppendView.swift in Sources */,
EB6530FB2C41CF8D00A66B7C /* WebIndicatorView.swift in Sources */,
EBBB832B2C60EC3A002F2892 /* Date.swift in Sources */,
EBEC3ABB2C3D0F3C0016F878 /* Toolbar.swift in Sources */,
Expand Down Expand Up @@ -706,7 +713,7 @@
EBE833532C3C8D9500FD8A73 /* Ripple.swift in Sources */,
EBD293D32C43945000FFB285 /* AppShortcuts.swift in Sources */,
EB62B5852C5FEB0700188A9D /* FileListView.swift in Sources */,
EBEAAACA2C467DD60026C328 /* DropFeature.swift in Sources */,
EBEAAACA2C467DD60026C328 /* DropAndImportFeature.swift in Sources */,
EBB37A982C40EFCB00D04474 /* DetectingHTTPView.swift in Sources */,
EB8190CA2C4571F8007EDE72 /* GradientBackgroundAnimation.swift in Sources */,
EB17F1F62C48169A00ED297A /* FileRowView.swift in Sources */,
Expand All @@ -720,6 +727,7 @@
EB59362F2C4053D200088AB9 /* Others.swift in Sources */,
EBE8334C2C3C8AD400FD8A73 /* Hover.swift in Sources */,
EB62B58F2C5FED3800188A9D /* ShortcutsView.swift in Sources */,
EBC7C85E2C9DD31B00B330D3 /* DropAndAppendFeature.swift in Sources */,
EBBB83292C60E673002F2892 /* NameFeature.swift in Sources */,
EBB794882C41146000D5AE1C /* PProfRowView.swift in Sources */,
EB1F1FC32C45B2B300FD585E /* Color.swift in Sources */,
Expand All @@ -731,7 +739,7 @@
EB85AB892C3DCC670080200A /* FailureFeature.swift in Sources */,
EB1003ED2C4A9860009E61F2 /* NotificationFeature.swift in Sources */,
EB1F1FC52C45B38D00FD585E /* Color+Luminance.swift in Sources */,
EB6530F52C41C09800A66B7C /* DropView.swift in Sources */,
EB6530F52C41C09800A66B7C /* DropAndImportView.swift in Sources */,
EB85AB852C3DCC2B0080200A /* LaunchingFeature.swift in Sources */,
EB8624A92C47B19D0010E153 /* File.swift in Sources */,
EBFC00EF2C3D0AFE004A1B93 /* Delay.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion approf/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct ContentView: View {
NavigaionView(store: store)
.onHover { uiState.mouseInsideWindow = $0 }
.overlay {
DropView(store: store.scope(state: \.drop, action: \.drop))
DropAndImportView(store: store.scope(state: \.drop, action: \.drop))
}
}
}
3 changes: 2 additions & 1 deletion approf/State/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation
@Observable
class UIState {
static var shared = UIState()
var mouseInsideWindow = true
private init() {}

var mouseInsideWindow = true
}
6 changes: 3 additions & 3 deletions approf/View/AppFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct AppFeature {
var pprofs: IdentifiedArrayOf<DetailFeature.State> = .init()
var pprofsSelectedId: UUID?

var drop: DropFeature.State = .init()
var drop: DropAndImportFeature.State = .init()
}

enum Action {
Expand All @@ -27,15 +27,15 @@ struct AppFeature {
case deleteNow(UUID)
case pprofs(IdentifiedActionOf<DetailFeature>)

case drop(DropFeature.Action)
case drop(DropAndImportFeature.Action)
}

@Dependency(\.uuid) var uuid
@Dependency(\.continuousClock) var clock

var body: some ReducerOf<Self> {
Scope(state: \.drop, action: \.drop) {
DropFeature()
DropAndImportFeature()
}

Reduce { state, action in
Expand Down
25 changes: 25 additions & 0 deletions approf/View/DragNDrop/DropAndAppendFeature.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import ComposableArchitecture
import Foundation

@Reducer
struct DropAndAppendFeature {
@ObservableState
struct State: Equatable {
@Shared var basic: PProfBasic
}

enum Action {
case onDropEnds([URL])
}

var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case let .onDropEnds(urls):
let u = urls.map { $0.path(percentEncoded: false) }
state.basic.filePaths.append(contentsOf: u)
return .none
}
}
}
}
19 changes: 19 additions & 0 deletions approf/View/DragNDrop/DropAndAppendView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Combine
import ComposableArchitecture
import SwiftUI
import UniformTypeIdentifiers

struct DropAndAppendView: View {
@Bindable var store: StoreOf<UnderTheHood>

@State var dropping = false

var body: some View {
Rectangle()
.fill(.clear)
.allowsHitTesting(false)
.onDrop(of: allowedImportFileTypes, delegate: DropFileDelegate(dropping: $dropping) { urls in
store.send(.onAddURLs(urls))
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,19 @@ import ComposableArchitecture
import Foundation

@Reducer
struct DropFeature {
struct DropAndImportFeature {
@Reducer(state: .equatable)
enum Destination {
case uth(UnderTheHood)
}

@ObservableState
struct State: Equatable {
var dropping = false
@Presents var destination: Destination.State?
}

enum Action {
case destination(PresentationAction<Destination.Action>)
case onCursorEnter
case onCursorLeave
case onDropEnds([URL])

case delegate(Delegate)
Expand All @@ -39,12 +36,6 @@ struct DropFeature {
switch action {
case .delegate:
return .none
case .onCursorEnter:
state.dropping = true
return .none
case .onCursorLeave:
state.dropping = false
return .none
case let .onDropEnds(urls):
let filePaths = urls.map { $0.path(percentEncoded: false) }
if filePaths.isEmpty {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import ComposableArchitecture
import SwiftUI
import UniformTypeIdentifiers

struct DropView: View {
@Bindable var store: StoreOf<DropFeature>
struct DropAndImportView: View {
@Bindable var store: StoreOf<DropAndImportFeature>

@State var viewSize = CGSize(width: 800, height: 600)
@State var dropping = false
@State var uiState = UIState.shared

var body: some View {
Rectangle()
.fill(.clear)
Expand All @@ -15,11 +18,16 @@ struct DropView: View {
} action: { viewSize = $0 }
.allowsHitTesting(false)
.overlay {
if store.dropping {
if dropping && store.destination == nil {
GradientBackgroundAnimation()
}
}
.onDrop(of: allowedImportFileTypes, delegate: DropFileDelegate(store: store))
.animation(.default, value: dropping)
.onDrop(of: allowedImportFileTypes, delegate: DropFileDelegate(dropping: $dropping) { urls in
if store.destination == nil{
store.send(.onDropEnds(urls))
}
})
.sheet(
item: $store.scope(state: \.destination?.uth, action: \.destination.uth)
) { importingStore in
Expand All @@ -33,28 +41,37 @@ struct DropView: View {
}

class DropFileDelegate: DropDelegate {
@Bindable var store: StoreOf<DropFeature>
@Binding var dropping: Bool
let onDropEnds: ([URL]) -> Void
let excludeArea: CGRect?

private var cancellables = Set<AnyCancellable>()

init(store: StoreOf<DropFeature>) {
self.store = store
init(dropping: Binding<Bool>, excludeArea: CGRect? = nil, onDropEnds: @escaping ([URL]) -> Void) {
_dropping = dropping
self.excludeArea = excludeArea
self.onDropEnds = onDropEnds
}

func validateDrop(info: DropInfo) -> Bool {
if case .uth = store.destination {
return false
}
return true
}

func dropEntered(info: DropInfo) {
store.send(.onCursorEnter, animation: .default)
if let excludeArea = excludeArea, excludeArea.contains(info.location) {
dropping = false
} else {
dropping = true
}
}

// func dropUpdated(info: DropInfo) -> DropProposal? {}
func dropUpdated(info: DropInfo) -> DropProposal? {
dropEntered(info: info)
return nil
}

func dropExited(info: DropInfo) {
store.send(.onCursorLeave, animation: .default)
dropping = false
}

func performDrop(info: DropInfo) -> Bool {
Expand Down Expand Up @@ -88,11 +105,10 @@ class DropFileDelegate: DropDelegate {
let urls = result.compactMap { $0 } as [URL]
log.info("imported \(urls.count) files")
Task { @MainActor in
self.store.send(.onDropEnds(urls))
self.onDropEnds(urls)
}
}
.store(in: &cancellables)

return true
}
}
File renamed without changes.
6 changes: 5 additions & 1 deletion approf/View/UnderTheHood/FileListView.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
// Created for approf in 2024

import ComposableArchitecture
import Foundation
import SwiftUI

struct FileListView: View {
@Bindable var store: StoreOf<UnderTheHood>
var importing: Bool = false
@State var uiState = UIState.shared

var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .firstTextBaseline, spacing: 4) {
Image(systemName: "folder")
// .foregroundColor(.blue)
Text("Files")
.foregroundStyle(.secondary)
}
Expand Down Expand Up @@ -78,6 +79,9 @@ struct FileListView: View {
.strokeBorder(.secondary.opacity(0.5), lineWidth: 0.5)
)
}
.overlay{
DropAndAppendView(store: store)
}
}

@ViewBuilder
Expand Down
5 changes: 5 additions & 0 deletions approf/View/UnderTheHood/UTH.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ struct UnderTheHood {
case onNameChanged(String)
case onPresentationChanged(PProfPresentation)
case onSelectFilesEnd([String])
case onAddURLs([URL])

case onMove(from: IndexSet, to: Int)
case onDeleteSelectedCommand
Expand Down Expand Up @@ -62,6 +63,10 @@ struct UnderTheHood {
case let .onSelectFilesEnd(filePaths):
addFiles(&state, filePaths: filePaths)
return .none
case let .onAddURLs(urls):
let u = urls.map { $0.path(percentEncoded: false) }
addFiles(&state, filePaths: u)
return .none
case let .onMove(from, to):
move(&state, from, to)
return .none
Expand Down
Loading

0 comments on commit 5e317d5

Please sign in to comment.