Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: drag and drop over file list #24

Merged
merged 2 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
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