From ce10b9570be78a6cee95ca7dc34c8033ba55fe10 Mon Sep 17 00:00:00 2001 From: Alin Date: Sat, 20 Jul 2024 10:32:27 -0600 Subject: [PATCH] v3.8.2 --- Pearcleaner.xcodeproj/project.pbxproj | 8 +- Pearcleaner/PearcleanerApp.swift | 3 +- .../pear-1.5.imageset/Contents.json | 21 - .../pear-1.5.imageset/pear-1.5.png | Bin 561 -> 0 bytes .../pear-1.imageset/Contents.json | 21 - .../pear-1.imageset/pear-1.png | Bin 565 -> 0 bytes .../pear-2.imageset/Contents.json | 21 - .../pear-2.imageset/pear-2.png | Bin 1028 -> 0 bytes .../pear-3.imageset/Contents.json | 21 - .../pear-3.imageset/pear-3.png | Bin 1129 -> 0 bytes .../pear-4.imageset/Contents.json | 21 - .../pear-4.imageset/pear-4.png | Bin 1030 -> 0 bytes Pearcleaner/Settings/About.swift | 6 +- Pearcleaner/Settings/Folders.swift | 10 +- Pearcleaner/Settings/General.swift | 383 ++++---- Pearcleaner/Settings/Interface.swift | 818 +++++++----------- Pearcleaner/Settings/SettingsWindow.swift | 2 + Pearcleaner/Settings/Tips.swift | 6 +- Pearcleaner/Settings/Update.swift | 15 +- Pearcleaner/Views/AppSearchView.swift | 10 +- Pearcleaner/Views/MenuBarItem.swift | 8 +- 21 files changed, 553 insertions(+), 821 deletions(-) delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-1.5.imageset/Contents.json delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-1.5.imageset/pear-1.5.png delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-1.imageset/Contents.json delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-1.imageset/pear-1.png delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-2.imageset/Contents.json delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-2.imageset/pear-2.png delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-3.imageset/Contents.json delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-3.imageset/pear-3.png delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-4.imageset/Contents.json delete mode 100644 Pearcleaner/Resources/Assets.xcassets/pear-4.imageset/pear-4.png diff --git a/Pearcleaner.xcodeproj/project.pbxproj b/Pearcleaner.xcodeproj/project.pbxproj index c8d54cd..2da9585 100644 --- a/Pearcleaner.xcodeproj/project.pbxproj +++ b/Pearcleaner.xcodeproj/project.pbxproj @@ -529,8 +529,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 55; - APP_VERSION = 3.8.1; + APP_BUILD = 56; + APP_VERSION = 3.8.2; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -599,8 +599,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - APP_BUILD = 55; - APP_VERSION = 3.8.1; + APP_BUILD = 56; + APP_VERSION = 3.8.2; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/Pearcleaner/PearcleanerApp.swift b/Pearcleaner/PearcleanerApp.swift index 7865527..771f625 100644 --- a/Pearcleaner/PearcleanerApp.swift +++ b/Pearcleaner/PearcleanerApp.swift @@ -103,10 +103,11 @@ struct PearcleanerApp: App { if menubarEnabled { MenuBarExtraManager.shared.addMenuBarExtra(withView: { MiniAppView(search: $search, showPopover: $showPopover, isMenuBar: true) - .environmentObject(locations) .environmentObject(appState) + .environmentObject(locations) .environmentObject(fsm) .environmentObject(themeManager) + .environmentObject(updater) .environmentObject(permissionManager) .preferredColorScheme(themeManager.displayMode.colorScheme) }, icon: selectedMenubarIcon) diff --git a/Pearcleaner/Resources/Assets.xcassets/pear-1.5.imageset/Contents.json b/Pearcleaner/Resources/Assets.xcassets/pear-1.5.imageset/Contents.json deleted file mode 100644 index 01d3ea9..0000000 --- a/Pearcleaner/Resources/Assets.xcassets/pear-1.5.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "pear-1.5.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Pearcleaner/Resources/Assets.xcassets/pear-1.5.imageset/pear-1.5.png b/Pearcleaner/Resources/Assets.xcassets/pear-1.5.imageset/pear-1.5.png deleted file mode 100644 index 0f348ca6959e06034287915d901d792e392bfde5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 561 zcmV-10?z%3P)Px$>q$gGR5(v%lRs+}K@`P*XWlak28$Hhq!EJRXNZN3Ru(E6L@}L$XeVl=f+VFy z8jExiArOSu1dOGHq{s)b)WXU_R7|1x=IEW-S>MBjg=OxY^Y7jjfB++$$1@g}1lYF{iMN1%P166HFg#nXqXC+ ze8Ol)aJ*CuS*#4DM=orYGMIaSBBe${&iMjj!!TPs2ctX^iY$IL%~JsVVPNO{07aM@ z7NvzvyTFo?K6T{HM6I{S_EGF$KUd^+ZNNA1t)#S7+e3e)jVMZ6H_Cg!Q{bRWPGO<5 zjdn5DQ__dF57l~IhM2%f!!}}}K=x^O=}6xM(6(AlqD zy~)>*-LP08$6{*$%hKRy z*qqcxwvWkv1)Pr3zahllg-!Mmx0STfCO5Ilon`wh_8M@ia>k6x*FKEb(eSaPA5#PF zLx?{t=VVwk+(~c-I0C!}UVvXF4ZgJhASC?(`hn^lpULnr00000NkvXXu0mjf)8qyq diff --git a/Pearcleaner/Resources/Assets.xcassets/pear-1.imageset/Contents.json b/Pearcleaner/Resources/Assets.xcassets/pear-1.imageset/Contents.json deleted file mode 100644 index 2cb0c80..0000000 --- a/Pearcleaner/Resources/Assets.xcassets/pear-1.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "pear-1.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Pearcleaner/Resources/Assets.xcassets/pear-1.imageset/pear-1.png b/Pearcleaner/Resources/Assets.xcassets/pear-1.imageset/pear-1.png deleted file mode 100644 index d8daa107c4a30995e1ee425f00eb424779559ec0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 565 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf66p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F_10?ke~(c|DE@{fI{~@T^vI+&gWj-=;iDv(E6}HhI7%u1qoXMI((Tn`X_D> zny)yumw!T#fcHkBH8l(e-2~!+SZB)>CxO!uT3SknqEfEzyNgSex^L zq4et1t40e_ZNffF{cmjBxV^y7GpO0x?mim~+2-TL0KIS^nbIfFB#vwOC*6 z++kqY^(Wc>mkxvfyvxt$bjP%XOCPnpc315!^H!yRjjxVe*qz;65n02$ZIM;VYv$7X zK(DHnxJHzuB$lLFB^RXvDF!10BNJT%V_idw5JPh-Q&TGwb8Q0yD+2>V!|B{88glbf zGSez?YjDWX-wxEE0k@$fGdH!kBr&%DU5}ZSp)te~hllfJfO;fBdV=%QO3D+9QW?t2 i%k?tzvWt@w3sUv+i_&MmvylQSV(@hJb6Mw<&;$S;e8STJ diff --git a/Pearcleaner/Resources/Assets.xcassets/pear-2.imageset/Contents.json b/Pearcleaner/Resources/Assets.xcassets/pear-2.imageset/Contents.json deleted file mode 100644 index 149905f..0000000 --- a/Pearcleaner/Resources/Assets.xcassets/pear-2.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "pear-2.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Pearcleaner/Resources/Assets.xcassets/pear-2.imageset/pear-2.png b/Pearcleaner/Resources/Assets.xcassets/pear-2.imageset/pear-2.png deleted file mode 100644 index c12b8e2576c0eedd9e645d721a027d818a0123c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1028 zcmaJ;dq|T}6hCvzw^nMIOUMGvu-P{sY0Gq)%MIt)rdWa1=G=6e`TAtFOx8oO3@VGB zLhq41+|slxn~&6p><=k|FiOkH`XfS=%Cy`0z9=ZD%emh@=iKu%y#ACH&ie)9giTLRGkK34gs+20hq*;WfY*C0{EZ-5FH2bFRs6>iUZ)} zDdiawERfrj1&$&@h`donzKoJqAdNuUA=RcNO(3-^$s~{#AU!}j;nBbxauY}|kb6Ly zf!Kgt1JVuTDv&lH*MZyy;sDYBis|gxy6v8 zDea2<=GC>$(QjIOFo`S=9P?gxc`|+zs|3X$SNks?1!KO84N($mzydVXh9c3yf=)KX z>F%;>5sp`gy1|B6fHPu3Yu6fuVSBYnxy8=WiwFtY@wC)Rc*Do1ShA;P2>u)m_046bJU7O!q|+PIhWaGIzG8 zd<+A(V(I!aJWH6(2~4n<&~c$5c$-Ddc^)_SsPTQ~ z)2#McocK!4{K4^Y&z0K~6YZ9R_T4(O*=#)=`NS{Ye{E30s<6RF8+eO`hTTTqjf|ML z)h6*O#8d54`<*rk?Uq{$q$wM94)OE=}Pr@ zKm-*LC7>b&!q^O9j3_!<6crOjQ6h>G3XggG0VpiWF&ei08zA1i>?Q(2e>2d=LVX#n zEiC>Uvs@&M{7E7nJ{^dd00xs@WS~vjQazYVrf}nyf-;@9SRYuIqI|BSX>6yI@CV#h_PotI^|Uy@jLOlI>L`9rr&wx5&UaY1ay8Ih`a8apqE zZ@VnD^OVrei{jgX=#u!3D`Go949RU5#P?j4+V#a?+fjktSEY7blGuJ;Y}*N;?H47R zVtj%20#UVfOlwpS5FLKbJF(PtQiaRDyTV&9OKra>wr-Eonr*5(fCd6JUXO%>=i zps~9y%bk7fz2l-ZP;kq6k^MKcw_lV3I(6#>L7+P?JPiOU+j?Gd`#ByU8&ui`;LeNkfnWupU^jrLzQIC0+|=-_>qb%EXodi?+Y|D^)e zUci_uE(!7j#X50-CDxsJektdqK7Qu>=kGc>)b75}FZlJRTJiWrUEUeLSBfwdeE(I= ztaSR4rqk>9ZVoT)6;2$KX3YKaA%QFV?ke~(c|DE@{fI^o&T^vIsE+;22FuM8b z=Blx&99Xk>_3=Ze*q(1LejzO(RakV8bw{tS&)GA&6$v$QcHXNW31v%{)p5i-1f00G zZp)c_ju9>*Itpso#EH7T)SFTaq2`9%bq>8ucW1B&x)@3 z`^S-~h_P{P?ps;exv_U04;m@Bx3oCG1;k>t<%wAIeI<)k_*0OGP{!?5vjY z9D3i;8_d9^aZFvs>4)b@uTu_;qF1L}+47~YujkB~9MjSb*G-CwK7SJC;_7y3aARO_ zG_cg)yV~3Y=sndE*NBpo#FA92N>oK!3G=^B>@Nm8iP>&=?PjG%( pNqJ&XDnogBxn5>oc5!lIL8@MUQTpt6Hc~)E44$rjF6*2UngC4K&CUP- diff --git a/Pearcleaner/Resources/Assets.xcassets/pear-4.imageset/Contents.json b/Pearcleaner/Resources/Assets.xcassets/pear-4.imageset/Contents.json deleted file mode 100644 index 3a23ed9..0000000 --- a/Pearcleaner/Resources/Assets.xcassets/pear-4.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "pear-4.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Pearcleaner/Resources/Assets.xcassets/pear-4.imageset/pear-4.png b/Pearcleaner/Resources/Assets.xcassets/pear-4.imageset/pear-4.png deleted file mode 100644 index f03db18bc1364174e190abd30f75e77a7201d509..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1030 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=IjczVPIe^4DbnY1uB?++H=YV+f5~+8w-Rs6^YEed^7&kg3UmK$AcQ~S2f>gjxg$8+@_&e3}~Tldjiz3&?>U#&7JWnlQS)#}Ga%jsu4 zryg^gbijG)A*Y#_eP*8X02Ap)pyxetM#3V zOJle`E;D_xz~J=~!&gfUe{8n;xxxDRB7-OMK(6|}*$U`Rhzo&SIOEwO1E5QQA|N*} zG6cB?$lhrFY=Hqt*CtDd2t>`3`A|Wiu@LcZo2;IKjeW7$@XI=Lh!zMr^{Cs&HD*6H zTYd(MKV4w(Wxe_H#fINDT0WSq`*wxN!#TP@V}a&;T5I-Zh4KIY{}Zc@Tmc4pR!NW_ zD8h&Vb`RGni)~vWD8yua=w)Rpw|r46JG1}VtyYYN_s=Ml{QeR!=TDv2ql-ogr#A^V ze2ZH$ZP~o8-+5}sHcA>SdH?I{l~$$IyMG@6TE&>;?e0=#akVuI$l)yTh%5%ug&@o* zW8Y8?WU!Zb`ns~eU=|Xz;Qhbzeiu+^pQnpsh{WaO1Rw~xe&I?|Qd(ka1Cv?qT%B0k zTIL-L*6j-?ZtPqs8PhPi`}Unfm%7@;)eam;&rQxwe}8~qT%e%j)Vabk!IqD$EHQVB zKHsrrX;rKd3^Y9dr^!Y?zmU^&L&KCU{rQQ3ar5Ty=W%f`Q`oRWg`cCTNpabU)+y6c zn^~L=D=>X}c=YK`g~mM`9Su$@J_=k)3nXG=!WbVNKjOr~^5~Jbc4N)@78lhG4V%1z zioCL-s@#^Xl453Nh!)em$i85C0?;R_C9V-ADTyViR>?)FK#IZ0z{o_`z*yJNBE-<# z%GA`##6sJ^z{nC}Q!>*kackJ75+4WDpaHj`Br`X)xFj*R09}unm7y`j z5{HNLWq^7lL3)Do(@M${i&7cN%ggmL^RkPR6AM!H@{7`Ezq647Dq`?-^>bP0l+XkK DIbo)% diff --git a/Pearcleaner/Settings/About.swift b/Pearcleaner/Settings/About.swift index 0937a71..ccae8d7 100644 --- a/Pearcleaner/Settings/About.swift +++ b/Pearcleaner/Settings/About.swift @@ -24,7 +24,7 @@ struct AboutSettingsTab: View { VStack(alignment: .center, spacing: 10) { Image(nsImage: NSApp.applicationIconImage) - .padding(.top, 30) +// .padding(.top, 20) Text(Bundle.main.name) .font(.title) .bold() @@ -141,10 +141,12 @@ struct AboutSettingsTab: View { .padding(5) .padding(.leading) + Spacer() + Text("Made with ❤️ by Alin Lupascu").font(.footnote)//.padding(.bottom) } .padding(20) - .frame(width: 500) + .frame(width: 500, height: 500) } } diff --git a/Pearcleaner/Settings/Folders.swift b/Pearcleaner/Settings/Folders.swift index 6efe027..6f77efc 100644 --- a/Pearcleaner/Settings/Folders.swift +++ b/Pearcleaner/Settings/Folders.swift @@ -17,6 +17,8 @@ struct FolderSettingsTab: View { @EnvironmentObject var fsm: FolderSettingsManager @EnvironmentObject var themeManager: ThemeManager @State private var isHovered = false + @AppStorage("settings.general.glass") private var glass: Bool = false + var body: some View { Form { @@ -79,7 +81,7 @@ struct FolderSettingsTab: View { .padding() .frame(height: 200) // .background(.primary.opacity(0.05)) - .background(backgroundView(themeManager: themeManager, darker: true)) + .background(backgroundView(themeManager: themeManager, darker: true, glass: glass)) .clipShape(RoundedRectangle(cornerRadius: 10)) .onDrop(of: ["public.file-url"], isTargeted: nil) { providers -> Bool in providers.forEach { provider in @@ -172,7 +174,7 @@ struct FolderSettingsTab: View { .scrollIndicators(.automatic) .padding() .frame(height: 200) - .background(backgroundView(themeManager: themeManager, darker: true)) + .background(backgroundView(themeManager: themeManager, darker: true, glass: glass)) // .background(.primary.opacity(0.05)) .clipShape(RoundedRectangle(cornerRadius: 10)) .onDrop(of: ["public.file-url"], isTargeted: nil) { providers -> Bool in @@ -199,9 +201,11 @@ struct FolderSettingsTab: View { .padding(.top, 5) } + Spacer() + } .padding(20) - .frame(width: 500)//, height: 600) + .frame(width: 500, height: 580) } diff --git a/Pearcleaner/Settings/General.swift b/Pearcleaner/Settings/General.swift index 86c29db..480975f 100644 --- a/Pearcleaner/Settings/General.swift +++ b/Pearcleaner/Settings/General.swift @@ -23,220 +23,219 @@ struct GeneralSettingsTab: View { @State private var isResetting = false var body: some View { - Form { - VStack { + VStack() { - HStack() { - Text("Functionality").font(.title2) - Spacer() - } - .padding(.leading) + HStack() { + Text("Functionality").font(.title2) + Spacer() + } + .padding(.leading) - HStack(spacing: 0) { - Image(systemName: brew ? "mug.fill" : "mug") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(brew ? "Homebrew cleanup is enabled" : "Homebrew cleanup is disabled")") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - - InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and execute a brew uninstall and brew cleanup command as well to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed.\n\nNOTE: If you undo the file delete with CMD+Z, the files will be put back but homebrew will not be aware of it. To get the homebrew list back in sync you'd need to run:\n brew install APPNAME --force") - - Spacer() - Toggle(isOn: $brew, label: { - }) - .toggleStyle(.switch) - } - .padding(5) - .padding(.leading) - - - HStack(spacing: 0) { - Image(systemName: oneShotMode ? "scope" : "circlebadge") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(oneShotMode ? "One-Shot Mode is enabled" : "One-Shot Mode is disabled")") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - - InfoButton(text: "When one-shot mode is enabled, clicking the Uninstall button to remove an app will also close Pearcleaner right after. This only affects Pearcleaner when it is opened via external means, like Sentinel Trash Monitor or the Finder extension. This allows for single use of the app, AKA one-shot mode. When Pearcleaner is opened normally, this setting is ignored and will work as usual.") - - Spacer() - Toggle(isOn: $oneShotMode, label: { - }) - .toggleStyle(.switch) - } - .padding(5) - .padding(.leading) - - - HStack(spacing: 0) { - Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("File list sorting mode") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - InfoButton(text: "When searching for app files or leftover files, the list will be sorted either alphabetically or by size(large to small)") - Spacer() - Picker("", selection: $selectedSortAlpha) { - Text("Alpha") - .tag(true) - Text("Size") - .tag(false) - } - .buttonStyle(.borderless) - } - .padding(5) - .padding(.leading) - - - HStack(spacing: 0) { - Image(systemName: "plus.forwardslash.minus") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("File size display mode") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - InfoButton(text: "Real size type will show how much actual allocated space the file has on disk. Logical type shows the binary size. The filesystem can compress and deduplicate sectors on disk, so real size is sometimes smaller(or bigger) than logical size. Finder size is similar to if you right click > Get Info on a file in Finder, which will show both the logical and real sizes together.") - Spacer() - Picker("", selection: $sizeType) { - Text("Real") - .tag("Real") - Text("Logical") - .tag("Logical") - Text("Finder") - .tag("Finder") - } - .buttonStyle(.borderless) - - } - .padding(5) - .padding(.leading) - - - - // === Sentinel ============================================================================================= - - Divider() - .padding() - - HStack() { - Text("Sentinel Monitor").font(.title2) - Spacer() - } - .padding(.leading) - - HStack(spacing: 0) { - Image(systemName: sentinel ? "eye.circle" : "eye.slash.circle") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(sentinel ? .green : .red) - .saturation(themeManager.displayMode.colorScheme == .dark ? 0.8 : 1) - Text(sentinel ? "Detecting when apps are moved to Trash" : "**NOT** detecting when apps are moved to Trash") + HStack(spacing: 0) { + Image(systemName: brew ? "mug.fill" : "mug") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(brew ? "Homebrew cleanup is enabled" : "Homebrew cleanup is disabled")") .font(.callout) .foregroundStyle(.primary.opacity(0.5)) - Spacer() - - Toggle(isOn: $sentinel, label: { - }) - .toggleStyle(.switch) - .onChange(of: sentinel) { newValue in - if newValue { - launchctl(load: true) - } else { - launchctl(load: false) - } - } - } - .padding(5) - .padding(.leading) + + InfoButton(text: "When homebrew cleanup is enabled, Pearcleaner will check if the app you are removing was installed via homebrew and execute a brew uninstall and brew cleanup command as well to let homebrew know that the app is removed. This way your homebrew list will be synced up correctly and caching will be removed.\n\nNOTE: If you undo the file delete with CMD+Z, the files will be put back but homebrew will not be aware of it. To get the homebrew list back in sync you'd need to run:\n brew install APPNAME --force") + + Spacer() + Toggle(isOn: $brew, label: { + }) + .toggleStyle(.switch) + } + .padding(5) + .padding(.leading) - - // === Finder Extension ============================================================================================= - - Divider() - .padding() - - HStack() { - Text("Finder Extension").font(.title2) - Spacer() - } - .padding(.leading) - - HStack(spacing: 0) { - Image(systemName: appState.finderExtensionEnabled ? "puzzlepiece.extension.fill" : "puzzlepiece.extension") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(appState.finderExtensionEnabled ? .green : .red) - .saturation(themeManager.displayMode.colorScheme == .dark ? 0.8 : 1) - Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled") + HStack(spacing: 0) { + Image(systemName: oneShotMode ? "scope" : "circlebadge") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(oneShotMode ? "One-Shot Mode is enabled" : "One-Shot Mode is disabled")") .font(.callout) .foregroundStyle(.primary.opacity(0.5)) - InfoButton(text: "Enabling the extension will allow you to right click apps in Finder and quickly uninstall them with Pearcleaner") - - Spacer() - - Button("Extensions") { - FIFinderSyncController.showExtensionManagementInterface() - } - .buttonStyle(SimpleButtonStyle(icon: "folder", help: "Show Extensions Pane")) - - } - .padding(5) - .padding(.leading) - // === Reset Settings ============================================================================================= + InfoButton(text: "When one-shot mode is enabled, clicking the Uninstall button to remove an app will also close Pearcleaner right after. This only affects Pearcleaner when it is opened via external means, like Sentinel Trash Monitor or the Finder extension. This allows for single use of the app, AKA one-shot mode. When Pearcleaner is opened normally, this setting is ignored and will work as usual.") - HStack() { - Spacer() + Spacer() + Toggle(isOn: $oneShotMode, label: { + }) + .toggleStyle(.switch) + } + .padding(5) + .padding(.leading) - Button("") { - resetUserDefaults() - } - .buttonStyle(ResetSettingsButtonStyle(isResetting: $isResetting, label: "Reset Settings", help: "Reset all app settings to default")) - .disabled(isResetting) - Spacer() + HStack(spacing: 0) { + Image(systemName: selectedSortAlpha ? "textformat.abc" : "textformat.123") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("File list sorting mode") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) } - .padding(.vertical, 5) + InfoButton(text: "When searching for app files or leftover files, the list will be sorted either alphabetically or by size(large to small)") + Spacer() + Picker("", selection: $selectedSortAlpha) { + Text("Alpha") + .tag(true) + Text("Size") + .tag(false) + } + .buttonStyle(.borderless) + } + .padding(5) + .padding(.leading) + + + HStack(spacing: 0) { + Image(systemName: "plus.forwardslash.minus") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("File size display mode") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + } + InfoButton(text: "Real size type will show how much actual allocated space the file has on disk. Logical type shows the binary size. The filesystem can compress and deduplicate sectors on disk, so real size is sometimes smaller(or bigger) than logical size. Finder size is similar to if you right click > Get Info on a file in Finder, which will show both the logical and real sizes together.") + Spacer() + Picker("", selection: $sizeType) { + Text("Real") + .tag("Real") + Text("Logical") + .tag("Logical") + Text("Finder") + .tag("Finder") + } + .buttonStyle(.borderless) } - .onAppear { - appState.updateExtensionStatus() + .padding(5) + .padding(.leading) + + + + // === Sentinel ============================================================================================= + + Divider() + .padding() + + HStack() { + Text("Sentinel Monitor").font(.title2) + Spacer() } + .padding(.leading) + + HStack(spacing: 0) { + Image(systemName: sentinel ? "eye.circle" : "eye.slash.circle") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(sentinel ? .green : .red) + .saturation(themeManager.displayMode.colorScheme == .dark ? 0.8 : 1) + Text(sentinel ? "Detecting when apps are moved to Trash" : "**NOT** detecting when apps are moved to Trash") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + Spacer() + + Toggle(isOn: $sentinel, label: { + }) + .toggleStyle(.switch) + .onChange(of: sentinel) { newValue in + if newValue { + launchctl(load: true) + } else { + launchctl(load: false) + } + } + + } + .padding(5) + .padding(.leading) + + + + // === Finder Extension ============================================================================================= + + Divider() + .padding() + + HStack() { + Text("Finder Extension").font(.title2) + Spacer() + } + .padding(.leading) + + HStack(spacing: 0) { + Image(systemName: appState.finderExtensionEnabled ? "puzzlepiece.extension.fill" : "puzzlepiece.extension") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(appState.finderExtensionEnabled ? .green : .red) + .saturation(themeManager.displayMode.colorScheme == .dark ? 0.8 : 1) + Text(appState.finderExtensionEnabled ? "Context menu extension for Finder is enabled" : "Context menu extension for Finder is disabled") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + InfoButton(text: "Enabling the extension will allow you to right click apps in Finder and quickly uninstall them with Pearcleaner") + + Spacer() + + Button("Extensions") { + FIFinderSyncController.showExtensionManagementInterface() + } + .buttonStyle(SimpleButtonStyle(icon: "folder", help: "Show Extensions Pane")) + + + } + .padding(5) + .padding(.leading) + + // === Reset Settings ============================================================================================= + + HStack() { + Spacer() + + Button("") { + resetUserDefaults() + } + .buttonStyle(ResetSettingsButtonStyle(isResetting: $isResetting, label: "Reset Settings", help: "Reset all app settings to default")) + .disabled(isResetting) + + Spacer() + } + .padding(.vertical, 5) + + Spacer() } + .onAppear { + appState.updateExtensionStatus() + } .padding(20) - .frame(width: 500)//, height: 710) + .frame(width: 500, height: 500) } diff --git a/Pearcleaner/Settings/Interface.swift b/Pearcleaner/Settings/Interface.swift index 7af1a88..e02d1e1 100644 --- a/Pearcleaner/Settings/Interface.swift +++ b/Pearcleaner/Settings/Interface.swift @@ -14,6 +14,8 @@ import AlinFoundation struct InterfaceSettingsTab: View { @EnvironmentObject var appState: AppState @EnvironmentObject var locations: Locations + @EnvironmentObject var updater: Updater + @EnvironmentObject var permissionManager: PermissionManager @EnvironmentObject var fsm: FolderSettingsManager @EnvironmentObject var themeManager: ThemeManager @State private var windowSettings = WindowSettings() @@ -22,546 +24,372 @@ struct InterfaceSettingsTab: View { @AppStorage("settings.general.glass") private var glass: Bool = false @AppStorage("settings.general.popover") private var popoverStay: Bool = true @AppStorage("settings.general.miniview") private var miniView: Bool = true - @AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "pear-4" + @AppStorage("settings.interface.selectedMenubarIcon") var selectedMenubarIcon: String = "bubbles.and.sparkles" @State private var isLaunchAtLoginEnabled: Bool = false @Binding var showPopover: Bool @Binding var search: String - let icons = ["externaldrive", "trash", "folder", "pear-1", "pear-1.5", "pear-2", "pear-3", "pear-4"] + let icons = [ "bubbles.and.sparkles", + "bubbles.and.sparkles.fill", + "trash", + "trash.fill", + "xmark.bin", + "xmark.bin.fill", + "folder", + "folder.fill", + "folder.badge.minus", + "folder.badge.plus", + "archivebox", + "archivebox.fill"] var body: some View { - Form { - VStack { + VStack { - HStack() { - Text("Appearance").font(.title2) - Spacer() - } - .padding(.leading) + HStack() { + Text("Appearance").font(.title2) + Spacer() + } + .padding(.leading) - HStack(spacing: 0) { - Image(systemName: glass ? "cube.transparent" : "cube.transparent.fill") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) + HStack(spacing: 0) { + Image(systemName: glass ? "cube.transparent" : "cube.transparent.fill") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(glass ? "Transparent material enabled" : "Transparent material disabled")") + .font(.callout) .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(glass ? "Transparent material enabled" : "Transparent material disabled")") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - Spacer() - Toggle(isOn: $glass, label: { - }) - .toggleStyle(.switch) - .onChange(of: glass) { newVal in + } + Spacer() + Toggle(isOn: $glass, label: { + }) + .toggleStyle(.switch) + .onChange(of: glass) { newVal in + MenuBarExtraManager.shared.restartMenuBarExtra() + } + } + .padding(5) + .padding(.leading) + + + HStack(spacing: 0) { + Image(systemName: "paintbrush") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("Theme Mode") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + } + Spacer() + ThemeSettingsView(opacity: 1) + .onChange(of: themeManager.displayMode) { _ in MenuBarExtraManager.shared.restartMenuBarExtra() } - } - .padding(5) - .padding(.leading) + } + .padding(5) + .padding(.leading) - HStack(spacing: 0) { - Image(systemName: "paintbrush") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) + + // === Mini ================================================================================================= + + Divider() + .padding() + + + + HStack() { + Text("Mini Configuration").font(.title2) + Spacer() + } + .padding(.leading) + + HStack(spacing: 0) { + Image(systemName: isVersionOrHigher(version: 14) ? "square.resize.up" : "macwindow.on.rectangle") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(mini ? "Mini window mode" : "Full size window mode")") + .font(.callout) .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("Theme Mode") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - Spacer() - ThemeSettingsView(opacity: 1) - .onChange(of: themeManager.displayMode) { _ in - MenuBarExtraManager.shared.restartMenuBarExtra() + } + Spacer() + Toggle(isOn: $mini, label: { + }) + .toggleStyle(.switch) + .disabled(menubarEnabled) + .help(menubarEnabled ? "Disabled when menubar icon is enabled" : "") + .onChange(of: mini) { newVal in + if mini { + appState.currentView = miniView ? .apps : .empty + showPopover = false + windowSettings.newWindow(withView: { + MiniMode(search: $search, showPopover: $showPopover) + .environmentObject(appState) + .environmentObject(locations) + .environmentObject(fsm) + .environmentObject(themeManager) + .environmentObject(updater) + .environmentObject(permissionManager) + .preferredColorScheme(themeManager.displayMode.colorScheme) + }) { + resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") + } + } else { + if appState.appInfo.appName.isEmpty { + appState.currentView = .empty + } else { + appState.currentView = .files + } + windowSettings.newWindow(withView: { + RegularMode(search: $search, showPopover: $showPopover) + .environmentObject(appState) + .environmentObject(locations) + .environmentObject(fsm) + .environmentObject(themeManager) + .environmentObject(updater) + .environmentObject(permissionManager) + .preferredColorScheme(themeManager.displayMode.colorScheme) + } + ) { + resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") } - } - .padding(5) - .padding(.leading) - - - -// HStack(spacing: 0) { -// Image(systemName: themesEnabled ? "paintpalette.fill" : "paintpalette") -// .resizable() -// .scaledToFit() -// .frame(width: 20, height: 20) -// .padding(.trailing) -// .foregroundStyle(.primary.opacity(0.5)) -// VStack(alignment: .leading, spacing: 5) { -// Text("\(themesEnabled ? "Custom theming is enabled" : "Custom theming is disabled")") -// .font(.callout) -// .foregroundStyle(.primary.opacity(0.5)) -// } -// Spacer() -// Toggle(isOn: $themesEnabled, label: { -// }) -// .toggleStyle(.switch) -// .onChange(of: themesEnabled) { newVal in -// themeManager.resetToDefault(dark: isDarkMode()) -// if isDarkMode() { -// displayMode.colorScheme = .dark -// } else { -// displayMode.colorScheme = .light -// } -// if newVal { -// selectedTheme = isDarkMode() ? "Dark" : "Light" -// } else { -// selectedTheme = "Auto" -// } -// } -// } -// .padding(5) -// .padding(.leading) -// -// -// /// Show theme color selector if theming is enabled -// if themesEnabled { -// HStack(spacing: 0) { -// Image(systemName: "paintbrush") -// .resizable() -// .scaledToFit() -// .frame(width: 20, height: 20) -// .padding(.trailing) -// .foregroundStyle(.primary.opacity(0.5)) -// VStack(alignment: .leading, spacing: 5) { -// Text("Application theme color") -// .font(.callout) -// .foregroundStyle(.primary.opacity(0.5)) -// } -// -// Spacer() -// -// -// HStack(spacing: 10) { -// Button("") { -// themeManager.setPreset(preset: "pearcleaner", colorScheme: displayMode) -// } -//// .buttonStyle(PresetColor(fillColor: themeManager.getColorForPreset(preset: "pearcleaner", colorScheme: displayMode), label: "Pearcleaner")) -// -// Button("") { -// themeManager.setPreset(preset: "dracula", colorScheme: displayMode) -// } -//// .buttonStyle(PresetColor(fillColor: themeManager.getColorForPreset(preset: "dracula", colorScheme: displayMode), label: "Dracula")) -// -// Button("") { -// themeManager.setPreset(preset: "solarized", colorScheme: displayMode) -// } -//// .buttonStyle(PresetColor(fillColor: themeManager.getColorForPreset(preset: "solarized", colorScheme: displayMode), label: "Solarized")) -// -// Button("") { -// themeManager.setPreset(preset: "macOS", colorScheme: displayMode) -// } -//// .buttonStyle(PresetColor(fillColor: themeManager.getColorForPreset(preset: "macOS", colorScheme: displayMode), label: "macOS")) -// } -// -// -// -// -// Spacer() -// -// ColorPicker("", selection: $themeManager.themeColor, supportsOpacity: false) -// .onChange(of: themeManager.themeColor) { newValue in -// themeManager.saveThemeColor() -// } -// .padding(.horizontal, 5) -// -// Button("") { -// themeManager.resetToDefault(dark: isDarkMode()) -// if isDarkMode() { -// displayMode.colorScheme = .dark -// } else { -// displayMode.colorScheme = .light -// } -// selectedTheme = isDarkMode() ? "Dark" : "Light" -// } -// .buttonStyle(SimpleButtonStyle(icon: "arrow.uturn.left.circle", help: "Reset color to default")) -// -// } -// .padding(5) -// .padding(.leading) -// } - - - -// HStack(spacing: 0) { -// Image(systemName: { -// switch displayMode { -// case .dark: -// return "moon.fill" -// case .light: -// return "sun.max.fill" -// case .system: -// return "circle.righthalf.filled" -// } -// }()) -// .resizable() -// .scaledToFit() -// .frame(width: 20, height: 20) -// .padding(.trailing) -// .foregroundStyle(.primary.opacity(0.5)) -// VStack(alignment: .leading, spacing: 5) { -// Text(themesEnabled ? "Application font color" : "Application color mode") -// .font(.callout) -// .foregroundStyle(.primary.opacity(0.5)) -// } -// InfoButton(text: "When custom theming is disabled, you can set the application color mode to follow the operating system using Auto. Or manually set it to Light or Dark. When custom theming is enabled, this selector acts mainly as a font color picker between light or dark to match your custom theme color.") -// Spacer() -// Picker("", selection: $selectedTheme) { -// if !themesEnabled { -// Text("Auto") -// .tag("Auto") -// } -// Text(themesEnabled ? "Light" : "Dark") -// .tag("Dark") -// Text(themesEnabled ? "Dark" : "Light") -// .tag("Light") -// } -// .buttonStyle(.borderless) -// .onChange(of: selectedTheme) { newTheme in -// switch newTheme { -// case "Auto": -// if isDarkMode() { -// displayMode.colorScheme = .dark -// } else { -// displayMode.colorScheme = .light -// } -// if !themesEnabled { -// themeManager.resetToDefault(dark: isDarkMode()) -// } -// // Refresh foreground colors -// DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { -// self.selectedTab = .interface -// } -// if menubarEnabled{ -// MenuBarExtraManager.shared.restartMenuBarExtra() -// } -// case "Dark": -// displayMode.colorScheme = .dark -// if !themesEnabled { -// themeManager.resetToDefault(dark: true) -// } -// DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { -// self.selectedTab = .interface -// } -// if menubarEnabled{ -// MenuBarExtraManager.shared.restartMenuBarExtra() -// } -// case "Light": -// displayMode.colorScheme = .light -// if !themesEnabled { -// themeManager.resetToDefault(dark: false) -// } -// DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { -// self.selectedTab = .interface -// } -// if menubarEnabled{ -// MenuBarExtraManager.shared.restartMenuBarExtra() -// } -// default: -// break -// } -// } -// } -// .padding(5) -// .padding(.leading) - - - - // === Mini ================================================================================================= - - Divider() - .padding() - - - - HStack() { - Text("Mini Configuration").font(.title2) - Spacer() - } - .padding(.leading) - - HStack(spacing: 0) { - Image(systemName: isVersionOrHigher(version: 14) ? "square.resize.up" : "macwindow.on.rectangle") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(mini ? "Mini window mode" : "Full size window mode")") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) } - Spacer() - Toggle(isOn: $mini, label: { - }) - .toggleStyle(.switch) - .disabled(menubarEnabled) - .help(menubarEnabled ? "Disabled when menubar icon is enabled" : "") - .onChange(of: mini) { newVal in + + + } + } + .padding(5) + .padding(.leading) + + + HStack(spacing: 0) { + Image(systemName: miniView ? "square.grid.3x3.square" : "plus.square.dashed") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(miniView ? "Show apps list on startup" : "Show drop target on startup")") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + } + + InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.") + + Spacer() + Toggle(isOn: $miniView, label: { + }) + .toggleStyle(.switch) + .onChange(of: miniView) { newVal in + appState.currentView = newVal ? .apps : .empty + } + } + .padding(5) + .padding(.leading) + + + HStack(spacing: 0) { + Image(systemName: popoverStay ? "pin" : "pin.slash") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(popoverStay ? "Popover window will stay on top" : "Popover window will not stay on top")") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + } + + InfoButton(text: "In mini window mode, if you pin the Files popover on top, clicking away from the window will not dismiss it. Otherwise, it will dismiss by clicking anywhere outside the popover.") + + Spacer() + Toggle(isOn: $popoverStay, label: { + }) + .toggleStyle(.switch) + } + .padding(5) + .padding(.leading) + + + // === MenuBar=============================================================================================== + + Divider() + .padding() + + HStack() { + Text("Menubar").font(.title2) + Spacer() + } + .padding(.leading) + + + HStack(spacing: 0) { + Image(systemName: "menubar.rectangle") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(menubarEnabled ? "Menubar icon enabled" : "Menubar icon disabled")") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + } + InfoButton(text: "When menubar icon is enabled, the main app window and dock icon will be disabled since the app will be put in accessory mode.") + Spacer() + Toggle(isOn: $menubarEnabled, label: { + }) + .toggleStyle(.switch) + .onChange(of: menubarEnabled) { newVal in + if newVal { + MenuBarExtraManager.shared.addMenuBarExtra(withView: { + MiniAppView(search: $search, showPopover: $showPopover, isMenuBar: true) + .environmentObject(appState) + .environmentObject(locations) + .environmentObject(fsm) + .environmentObject(themeManager) + .environmentObject(updater) + .environmentObject(permissionManager) + .preferredColorScheme(themeManager.displayMode.colorScheme) + }, icon: selectedMenubarIcon) + NSApplication.shared.setActivationPolicy(.accessory) + findAndHideWindows(named: ["Pearcleaner"]) + // findAndShowWindows(named: ["Pearcleaner", "Interface"]) + } else { + MenuBarExtraManager.shared.removeMenuBarExtra() + NSApplication.shared.setActivationPolicy(.regular) + if !hasWindowOpen() { if mini { - appState.currentView = miniView ? .apps : .empty - showPopover = false windowSettings.newWindow(withView: { MiniMode(search: $search, showPopover: $showPopover) - .environmentObject(locations) .environmentObject(appState) + .environmentObject(locations) .environmentObject(fsm) .environmentObject(themeManager) + .environmentObject(updater) + .environmentObject(permissionManager) .preferredColorScheme(themeManager.displayMode.colorScheme) }) { resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") } } else { - if appState.appInfo.appName.isEmpty { - appState.currentView = .empty - } else { - appState.currentView = .files - } windowSettings.newWindow(withView: { RegularMode(search: $search, showPopover: $showPopover) - .environmentObject(locations) .environmentObject(appState) + .environmentObject(locations) .environmentObject(fsm) .environmentObject(themeManager) + .environmentObject(updater) + .environmentObject(permissionManager) .preferredColorScheme(themeManager.displayMode.colorScheme) - } - ) { + }) { resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") } } - - - } - } - .padding(5) - .padding(.leading) - - - HStack(spacing: 0) { - Image(systemName: miniView ? "square.grid.3x3.square" : "plus.square.dashed") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(miniView ? "Show apps list on startup" : "Show drop target on startup")") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - - InfoButton(text: "In mini window mode, you can have Pearcleaner startup to the Apps List view or the Drop Target view.") - - Spacer() - Toggle(isOn: $miniView, label: { - }) - .toggleStyle(.switch) - .onChange(of: miniView) { newVal in - appState.currentView = newVal ? .apps : .empty - } - } - .padding(5) - .padding(.leading) - - - HStack(spacing: 0) { - Image(systemName: popoverStay ? "pin" : "pin.slash") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(popoverStay ? "Popover window will stay on top" : "Popover window will not stay on top")") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - - InfoButton(text: "In mini window mode, if you pin the Files popover on top, clicking away from the window will not dismiss it. Otherwise, it will dismiss by clicking anywhere outside the popover.") - - Spacer() - Toggle(isOn: $popoverStay, label: { - }) - .toggleStyle(.switch) - } - .padding(5) - .padding(.leading) - - - // === MenuBar=============================================================================================== - - Divider() - .padding() - - HStack() { - Text("Menubar").font(.title2) - Spacer() - } - .padding(.leading) - - - HStack(spacing: 0) { - Image(systemName: "menubar.rectangle") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(menubarEnabled ? "Menubar icon enabled" : "Menubar icon disabled")") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - InfoButton(text: "When menubar icon is enabled, the main app window and dock icon will be disabled since the app will be put in accessory mode.") - Spacer() - Toggle(isOn: $menubarEnabled, label: { - }) - .toggleStyle(.switch) - .onChange(of: menubarEnabled) { newVal in - if newVal { - MenuBarExtraManager.shared.addMenuBarExtra(withView: { - MiniAppView(search: $search, showPopover: $showPopover, isMenuBar: true) - .environmentObject(locations) - .environmentObject(appState) - .environmentObject(fsm) - .environmentObject(themeManager) - .preferredColorScheme(themeManager.displayMode.colorScheme) - }, icon: selectedMenubarIcon) - NSApplication.shared.setActivationPolicy(.accessory) - findAndHideWindows(named: ["Pearcleaner"]) -// findAndShowWindows(named: ["Pearcleaner", "Interface"]) - } else { - MenuBarExtraManager.shared.removeMenuBarExtra() - NSApplication.shared.setActivationPolicy(.regular) - if !hasWindowOpen() { - if mini { - windowSettings.newWindow(withView: { - MiniMode(search: $search, showPopover: $showPopover) - .environmentObject(locations) - .environmentObject(appState) - .environmentObject(fsm) - .environmentObject(themeManager) - .preferredColorScheme(themeManager.displayMode.colorScheme) - }) { - resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") - } - } else { - windowSettings.newWindow(withView: { - RegularMode(search: $search, showPopover: $showPopover) - .environmentObject(locations) - .environmentObject(appState) - .environmentObject(fsm) - .environmentObject(themeManager) - .preferredColorScheme(themeManager.displayMode.colorScheme) - }) { - resizeWindowAuto(windowSettings: windowSettings, title: "Pearcleaner") - } - } - } - } - } + } } - .padding(5) - .padding(.leading) - - HStack(spacing: 0) { - Image(systemName: isLaunchAtLoginEnabled ? "person.fill" : "person") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("\(isLaunchAtLoginEnabled ? "Launch at login enabled" : "Launch at login disabled")") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - InfoButton(text: "This setting will affect Pearcleaner whether you're running in menubar mode or regular mode. If you disable menubar icon, you might want to disable this as well so Pearcleaner doesn't start on login.") - Spacer() - Toggle(isOn: $isLaunchAtLoginEnabled, label: { - }) - .toggleStyle(.switch) - .onAppear { - isLaunchAtLoginEnabled = SMAppService.mainApp.status == .enabled - } - .onChange(of: isLaunchAtLoginEnabled) { newValue in - do { - if newValue { - if SMAppService.mainApp.status == .enabled { - try? SMAppService.mainApp.unregister() - } - - try SMAppService.mainApp.register() - } else { - try SMAppService.mainApp.unregister() - } - } catch { - printOS("Failed to \(newValue ? "enable" : "disable") launch at login: \(error.localizedDescription)") - } - } - - } - .padding(5) - .padding(.leading) - - - HStack(spacing: 0) { - Image(systemName: "paintbrush") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .padding(.trailing) - .foregroundStyle(.primary.opacity(0.5)) - VStack(alignment: .leading, spacing: 5) { - Text("Menubar icon") - .font(.callout) - .foregroundStyle(.primary.opacity(0.5)) - } - Spacer() - Picker("", selection: $selectedMenubarIcon) { - ForEach(icons, id: \.self) { icon in - HStack { - if icon.contains("pear") { - Image(icon) - .resizable() - .scaledToFit() - } else { - Image(systemName: icon) - .resizable() - .scaledToFit() - } - } - .tag(icon) - - } - } - .frame(width: 60) - .onChange(of: selectedMenubarIcon) { newValue in - MenuBarExtraManager.shared.swapMenuBarIcon(icon: newValue) - } - .buttonStyle(.borderless) - - } - .padding(5) - .padding(.leading) - -// Spacer() } + .padding(5) + .padding(.leading) + + + HStack(spacing: 0) { + Image(systemName: isLaunchAtLoginEnabled ? "person.fill" : "person") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("\(isLaunchAtLoginEnabled ? "Launch at login enabled" : "Launch at login disabled")") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + } + InfoButton(text: "This setting will affect Pearcleaner whether you're running in menubar mode or regular mode. If you disable menubar icon, you might want to disable this as well so Pearcleaner doesn't start on login.") + Spacer() + Toggle(isOn: $isLaunchAtLoginEnabled, label: { + }) + .toggleStyle(.switch) + .onAppear { + isLaunchAtLoginEnabled = SMAppService.mainApp.status == .enabled + } + .onChange(of: isLaunchAtLoginEnabled) { newValue in + do { + if newValue { + if SMAppService.mainApp.status == .enabled { + try? SMAppService.mainApp.unregister() + } + + try SMAppService.mainApp.register() + } else { + try SMAppService.mainApp.unregister() + } + } catch { + printOS("Failed to \(newValue ? "enable" : "disable") launch at login: \(error.localizedDescription)") + } + } + + } + .padding(5) + .padding(.leading) + + + HStack(spacing: 0) { + Image(systemName: "paintbrush") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .padding(.trailing) + .foregroundStyle(.primary.opacity(0.5)) + VStack(alignment: .leading, spacing: 5) { + Text("Menubar icon") + .font(.callout) + .foregroundStyle(.primary.opacity(0.5)) + } + Spacer() + Picker("", selection: $selectedMenubarIcon) { + ForEach(icons, id: \.self) { icon in + HStack { + Image(systemName: icon) + .resizable() + .scaledToFit() + } + .tag(icon) + + } + } + .frame(width: 60) + .onChange(of: selectedMenubarIcon) { newValue in + MenuBarExtraManager.shared.swapMenuBarIcon(icon: newValue) + } + .buttonStyle(.borderless) + + } + .padding(5) + .padding(.leading) + + Spacer() } .padding(20) - .frame(width: 500)//, height: themesEnabled ? 600 : 580) + .frame(width: 500, height: 540) } diff --git a/Pearcleaner/Settings/SettingsWindow.swift b/Pearcleaner/Settings/SettingsWindow.swift index 1ecfabe..d345139 100644 --- a/Pearcleaner/Settings/SettingsWindow.swift +++ b/Pearcleaner/Settings/SettingsWindow.swift @@ -32,12 +32,14 @@ struct SettingsView: View { Label(CurrentTabView.interface.title, systemImage: "macwindow") } .tag(CurrentTabView.interface) + .environmentObject(themeManager) FolderSettingsTab() .tabItem { Label(CurrentTabView.folders.title, systemImage: "folder") } .tag(CurrentTabView.folders) + .environmentObject(themeManager) UpdateSettingsTab() .tabItem { diff --git a/Pearcleaner/Settings/Tips.swift b/Pearcleaner/Settings/Tips.swift index bc9fa77..50c4c3b 100644 --- a/Pearcleaner/Settings/Tips.swift +++ b/Pearcleaner/Settings/Tips.swift @@ -13,6 +13,7 @@ import AlinFoundation struct TipsSettingsTab: View { @EnvironmentObject var appState: AppState @EnvironmentObject var themeManager: ThemeManager + @AppStorage("settings.general.glass") private var glass: Bool = false var body: some View { VStack { @@ -40,14 +41,15 @@ struct TipsSettingsTab: View { .scrollIndicators(.visible) .frame(height: 500) .frame(maxWidth: .infinity) - .background(backgroundView(themeManager: themeManager, darker: true)) + .background(backgroundView(themeManager: themeManager, darker: true, glass: glass)) .clipShape(RoundedRectangle(cornerRadius: 8)) .padding(.bottom) + Spacer() } .padding(20) - .frame(width: 500) + .frame(width: 500, height: 550) } } diff --git a/Pearcleaner/Settings/Update.swift b/Pearcleaner/Settings/Update.swift index a7b7814..87788bb 100644 --- a/Pearcleaner/Settings/Update.swift +++ b/Pearcleaner/Settings/Update.swift @@ -14,6 +14,7 @@ struct UpdateSettingsTab: View { @EnvironmentObject var appState: AppState @EnvironmentObject var themeManager: ThemeManager @EnvironmentObject var updater: Updater + @AppStorage("settings.general.glass") private var glass: Bool = false var body: some View { VStack { @@ -25,8 +26,9 @@ struct UpdateSettingsTab: View { ReleasesView(updater: updater) .frame(height: 400) .frame(maxWidth: .infinity) - .backgroundAF(opacity: 0.5) - + .background(backgroundView(themeManager: themeManager, darker: true, glass: glass)) + .clipShape(RoundedRectangle(cornerRadius: 8)) + HStack(alignment: .center, spacing: 20) { @@ -48,11 +50,14 @@ struct UpdateSettingsTab: View { .buttonStyle(SimpleButtonStyle(icon: "link", label: "Releases", help: "View releases on GitHub")) } - .padding() - + .padding(.top, 5) + + Spacer() + } .padding(20) - .frame(width: 500) + .frame(width: 500, height: 540) + } } diff --git a/Pearcleaner/Views/AppSearchView.swift b/Pearcleaner/Views/AppSearchView.swift index f43742e..bfa1453 100644 --- a/Pearcleaner/Views/AppSearchView.swift +++ b/Pearcleaner/Views/AppSearchView.swift @@ -192,11 +192,11 @@ struct SimpleSearchStyle: TextFieldStyle { func _body(configuration: TextField) -> some View { ZStack { - RoundedRectangle(cornerRadius: 8) - .fill(darker ? themeManager.pickerColor.adjustBrightness(5) : themeManager.pickerColor) - .allowsHitTesting(false) - .frame(height: 30) - .opacity((glass && (sidebar || !mini && !menubarEnabled)) || mini || menubarEnabled ? 0.0 : 1.0) +// RoundedRectangle(cornerRadius: 8) +// .fill(darker ? themeManager.pickerColor.adjustBrightness(5) : themeManager.pickerColor) +// .allowsHitTesting(false) +// .frame(height: 30) +// .opacity((glass && (sidebar || !mini && !menubarEnabled)) || mini || menubarEnabled ? 0.0 : 1.0) ZStack { diff --git a/Pearcleaner/Views/MenuBarItem.swift b/Pearcleaner/Views/MenuBarItem.swift index a6d4f83..6682a56 100644 --- a/Pearcleaner/Views/MenuBarItem.swift +++ b/Pearcleaner/Views/MenuBarItem.swift @@ -12,7 +12,6 @@ class MenuBarExtraManager { static let shared = MenuBarExtraManager() private var statusItem: NSStatusItem? private var popover = NSPopover() - // Store the last view and icon for restart private var lastView: (() -> AnyView)? private var lastIcon: String? @@ -28,12 +27,7 @@ class MenuBarExtraManager { // Set up the status item's button if let button = statusItem?.button{ - if NSImage(systemSymbolName: icon, accessibilityDescription: nil) != nil { - button.image = NSImage(systemSymbolName: icon, accessibilityDescription: "Pearcleaner") - } else { - button.image = NSImage(named: icon) - - } + button.image = NSImage(systemSymbolName: icon, accessibilityDescription: "Pearcleaner") button.action = #selector(togglePopover(_:)) button.target = self button.sendAction(on: [.leftMouseDown, .rightMouseDown])