diff --git a/.gitignore b/.gitignore index 01cf0d1..9e84833 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /Packages /*.xcodeproj xcuserdata/ +.swiftpm .theos /packages diff --git a/Sources/Pinnacle/Hooks/SBIconListViewHook.x.swift b/Sources/Pinnacle/Hooks/SBIconListViewHook.x.swift index ea710ab..f1d83df 100644 --- a/Sources/Pinnacle/Hooks/SBIconListViewHook.x.swift +++ b/Sources/Pinnacle/Hooks/SBIconListViewHook.x.swift @@ -68,6 +68,7 @@ class SBIconListViewHook: ClassHook { // orion:new func _pinnacleForIconViews(_ action:(SBIconView) -> Void) { + if target.subviews.isEmpty { return } // Fixes a crash issue var iconViews: [UIView] if target.subviews[0].isKind(of: SBFTouchPassThroughView.classForCoder()) { iconViews = target.subviews[0].subviews diff --git a/Sources/Pinnacle/Hooks/SBIconViewHook.x.swift b/Sources/Pinnacle/Hooks/SBIconViewHook.x.swift index b9d7cdb..efde56d 100644 --- a/Sources/Pinnacle/Hooks/SBIconViewHook.x.swift +++ b/Sources/Pinnacle/Hooks/SBIconViewHook.x.swift @@ -4,6 +4,8 @@ import PinnacleC class SBIconViewHook: ClassHook { @Property var hasInit = false @Property var icon: SBApplicationIcon? + + @Property var grabberView: UIImageView? = nil @Property var row: UInt? = nil @Property var column: UInt? = nil @@ -14,7 +16,12 @@ class SBIconViewHook: ClassHook { func didMoveToSuperview() { orig.didMoveToSuperview() - guard !target.isFolderIcon() else { return } + guard !target.isFolderIcon() else { + if grabberView != nil { + grabberView?.removeFromSuperview() + } + return + } guard target.icon != nil else { return } guard target.icon.isKind(of: SBApplicationIcon.classForCoder()) else { return } icon = target.icon as? SBApplicationIcon @@ -23,6 +30,30 @@ class SBIconViewHook: ClassHook { guard !target.superview!.isKind(of: SBDockIconListView.classForCoder()) else { return } guard !target.isKind(of: PinnacleIconView.classForCoder()) else { return } + + if grabberImage == nil { + grabberImage = UIImage(contentsOfFile: grabberPath()) + } + + if grabberView == nil { + let iconSize = _pinnacleGetImageSize() + + grabberView = UIImageView(frame: CGRectMake(iconSize.width * -0.06, iconSize.height * -0.06, iconSize.width * 1.12, iconSize.height * 1.12)) + grabberView?.isUserInteractionEnabled = false + grabberView?.contentMode = .scaleAspectFit + } + + grabberView?.alpha = 1 + _pinnacleUpdateIndicator(bundleID: icon!.applicationBundleID()) + + if !grabberView!.isDescendant(of: target) { + target.addSubview(grabberView!) + } + target.sendSubviewToBack(grabberView!) + + _pinnacleForSubviews({(iconView: PinnacleIconView) -> Void in + iconView.removeFromSuperview() + }) guard !hasInit else { return } hasInit = true @@ -39,10 +70,54 @@ class SBIconViewHook: ClassHook { target.addGestureRecognizer(swipeGesture) scrollViewPanGesture?.require(toFail: swipeGesture) + + if settings!.activationGestureDirection == "both" { + let secondSwipeGesture = UISwipeGestureRecognizer() + secondSwipeGesture.direction = .up + + secondSwipeGesture.addTarget(target, action: #selector(_pinnacleHandleActivation)) + + target.addGestureRecognizer(secondSwipeGesture) + scrollViewPanGesture?.require(toFail: secondSwipeGesture) + } + } + + // orion:new + func _pinnacleUpdateIndicator(bundleID: String) { + if settings!.indicator == "none" { + grabberView?.image = nil + return + } + + let data = getStackData(for: bundleID) + var direction = 0 + + let offsets = [ + (0, -1), // Up + (1, 0), // Right + (0, 1), // Down + (-1, 0) // Left + ] + + for (index, bundle) in data.enumerated() { + direction = (direction + 1) % 4 + let (xDir, yDir) = offsets[direction] + + if !bundle.contains("dev.rugmj.PinnaclePlaceholder") { + if settings!.indicator == "apps" { + // TODO: App previews + } else { + grabberView?.image = grabberImage + return + } + } + } + + grabberView?.image = nil } // orion:new - @objc func _pinnacleHandleActivation() { + @objc func _pinnacleHandleActivation() { guard !target.isFolderIcon() else { return } guard !active else { return } active = true @@ -146,9 +221,9 @@ class SBIconViewHook: ClassHook { UIView.animate(withDuration: settings!.fadeDuration, animations: { self.target.alpha = 1 }) - + guard !target.icon.isKind(of: SBWidgetIcon.classForCoder()) else { return } - + if let x = originalX, let y = originalY { UIView.animate( withDuration: settings!.iconMoveDuration, @@ -161,12 +236,37 @@ class SBIconViewHook: ClassHook { self.target.frame.origin.x = x } } - - for subview in target.subviews { - if let iconView = subview as? PinnacleIconView { - iconView._pinnacleReset() + + if self.grabberView != nil { + if target.isFolderIcon() { + self.grabberView?.removeFromSuperview() + } else { + if !self.grabberView!.isDescendant(of: target) { + target.addSubview(self.grabberView!) + } + if icon!.applicationBundleID() != nil { + _pinnacleUpdateIndicator(bundleID: self.icon!.applicationBundleID()) + } else { + self.grabberView?.image = nil + } + target.sendSubviewToBack(self.grabberView!) + UIView.animate( + withDuration: settings!.iconMoveDuration, + delay: 0, + usingSpringWithDamping: settings!.springDamping, + initialSpringVelocity: settings!.springInitialVelocity + ) + { + self.grabberView?.alpha = 1 + } } } + + + _pinnacleForSubviews({(iconView: PinnacleIconView) -> Void in + iconView._pinnacleReset() + }) + } @@ -227,7 +327,17 @@ class SBIconViewHook: ClassHook { } else { yDiff *= directions[2] as! Int } - + + + UIView.animate( + withDuration: settings!.iconMoveDuration, + delay: 0, + usingSpringWithDamping: settings!.springDamping, + initialSpringVelocity: settings!.springInitialVelocity + ) + { + self.grabberView?.alpha = 0 + } _pinnacleMoveWith(x: xDiff, y: yDiff) } diff --git a/Sources/Pinnacle/PinnacleIconView.swift b/Sources/Pinnacle/PinnacleIconView.swift index 11afedc..fb82251 100644 --- a/Sources/Pinnacle/PinnacleIconView.swift +++ b/Sources/Pinnacle/PinnacleIconView.swift @@ -35,9 +35,11 @@ class PinnacleIconView: SBIconView { self.init() setupCommonProperties(xDir: xDir, yDir: yDir, bundle: bundle, parentBundle: parentBundle, index: index) - let image = UIImage(contentsOfFile: plusCirclePath()) + if placeholderImage == nil { + placeholderImage = UIImage(contentsOfFile: plusCirclePath()) + } - let imageView = UIImageView(image: image) + let imageView = UIImageView(image: placeholderImage) imageView.frame = CGRect(x: 0, y: 0, width: 60, height: 60) imageView.isUserInteractionEnabled = true diff --git a/Sources/Pinnacle/Tweak.x.swift b/Sources/Pinnacle/Tweak.x.swift index 015ce44..bf059fc 100644 --- a/Sources/Pinnacle/Tweak.x.swift +++ b/Sources/Pinnacle/Tweak.x.swift @@ -18,7 +18,7 @@ class Pinnacle: Tweak { }) }, name, nil, .deliverImmediately) - if settings!.activationGestureDirection == "down" { + if settings!.activationGestureDirection != "up" { SpotlightHookGroup().activate() } } @@ -102,3 +102,6 @@ func normalise(_ number: Int) -> Int { } var settings: Settings? + +var grabberImage: UIImage? = nil +var placeholderImage: UIImage? = nil diff --git a/Sources/Pinnacle/TweakPreferences.swift b/Sources/Pinnacle/TweakPreferences.swift index f4b2358..a9902aa 100644 --- a/Sources/Pinnacle/TweakPreferences.swift +++ b/Sources/Pinnacle/TweakPreferences.swift @@ -27,4 +27,5 @@ struct Settings: Codable { var springInitialVelocity = 0.0 var hapticFeedback = true var activationGestureDirection = "up" + var indicator = "none" } diff --git a/Sources/PinnacleC/Tweak.m b/Sources/PinnacleC/Tweak.m index 98bad0f..cd26cf6 100644 --- a/Sources/PinnacleC/Tweak.m +++ b/Sources/PinnacleC/Tweak.m @@ -11,3 +11,8 @@ return JBROOT_PATH_NSSTRING( @"/Library/Tweak Support/dev.rugmj.pinnacle/plus-circle.png"); } + +NSString *grabberPath() { + return JBROOT_PATH_NSSTRING( + @"/Library/Tweak Support/dev.rugmj.pinnacle/grabber.png"); +} diff --git a/Sources/PinnacleC/include/Tweak.h b/Sources/PinnacleC/include/Tweak.h index 2c7d53d..e13d589 100644 --- a/Sources/PinnacleC/include/Tweak.h +++ b/Sources/PinnacleC/include/Tweak.h @@ -17,6 +17,7 @@ #import NSString *plusCirclePath(); +NSString *grabberPath(); @interface SBSearchScrollView : UIScrollView @end diff --git a/control b/control index 62613e0..166f45f 100644 --- a/control +++ b/control @@ -1,6 +1,6 @@ Package: dev.rugmj.pinnacle Name: Pinnacle -Version: 1.1.0 +Version: 1.2.0 Architecture: iphoneos-arm Description: A new way to organise your apps Maintainer: rugmj diff --git a/layout/Library/Tweak Support/dev.rugmj.pinnacle/grabber.png b/layout/Library/Tweak Support/dev.rugmj.pinnacle/grabber.png new file mode 100644 index 0000000..c88db12 Binary files /dev/null and b/layout/Library/Tweak Support/dev.rugmj.pinnacle/grabber.png differ diff --git a/pinnacleprefs/Sources/PinnaclePrefs/Main/PreferenceStorage.swift b/pinnacleprefs/Sources/PinnaclePrefs/Main/PreferenceStorage.swift index 83b1fb8..7444444 100644 --- a/pinnacleprefs/Sources/PinnaclePrefs/Main/PreferenceStorage.swift +++ b/pinnacleprefs/Sources/PinnaclePrefs/Main/PreferenceStorage.swift @@ -5,8 +5,8 @@ import libroot final class PreferenceStorage: ObservableObject { static let registry: String = jbRootPath("/var/mobile/Library/Preferences/dev.rugmj.pinnacleprefs.plist") - @Published(key: "enabled", registry: registry) var isEnabled: Bool = true - @Published(key: "showAppLabels", registry: registry) var isEnabled: Bool = true + @Published(key: "enabled", registry: registry) var isEnabled: Bool = true + @Published(key: "showAppLabels", registry: registry) var showAppLabels: Bool = true @Published(key: "fadeAmount", registry: registry) var fadeAmount: Double = 0.3 @Published(key: "fadeDuration", registry: registry) var fadeDuration: Double = 0.2 @Published(key: "iconMoveDuration", registry: registry) var iconMoveDuration: Double = 0.5 @@ -14,6 +14,7 @@ final class PreferenceStorage: ObservableObject { @Published(key: "springInitialVelocity", registry: registry) var springInitialVelocity: Double = 0.0 @Published(key: "hapticFeedback", registry: registry) var hapticFeedback = true @Published(key: "activationGestureDirection", registry: registry) var activationGestureDirection = "up" + @Published(key: "indicator", registry: registry) var indicator = "none" private var cancellables: Set = [] diff --git a/pinnacleprefs/Sources/PinnaclePrefs/Main/RootView.swift b/pinnacleprefs/Sources/PinnaclePrefs/Main/RootView.swift index 9cd451a..4fa0046 100644 --- a/pinnacleprefs/Sources/PinnaclePrefs/Main/RootView.swift +++ b/pinnacleprefs/Sources/PinnaclePrefs/Main/RootView.swift @@ -42,11 +42,23 @@ struct RootView: View { Picker("Gesture Direction", selection: $preferenceStorage.activationGestureDirection) { Text("Up").tag("up") Text("Down").tag("down") + Text("Either").tag("both") }.pickerStyle(.segmented) } header: { Text("Gesture") } + + Section { + Picker("Visual Indicator", selection: $preferenceStorage.indicator) { + Text("None").tag("none") + Text("Grabbers").tag("grabbers") + //Text("App Previews").tag("apps") + }.pickerStyle(.segmented) + + } header: { + Text("Indicator") + } Section { SliderWithLabel(