diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
index 7cd56d0..f461f8c
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,28 @@
-## .DS_Store
+# macOS
.DS_Store
+.AppleDouble
+.LSOverride
-## No Pods folder
+# Xcode
+xcuserdata/
+*.xcuserstate
+*.xcscmblueprint
+DerivedData/
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+# Build output
+build/
+*.pock
+
+# Swift Package Manager
+.build/
+.swiftpm/
+
+# CocoaPods (not used — project uses SPM)
+Podfile
Podfile.lock
Pods/
-
-## Userdata
-Status.xcodeproj/xcuserdata/
-Status.xcworkspace/xcuserdata/
+*.xcworkspace
diff --git a/Podfile b/Podfile
deleted file mode 100644
index 71cd85e..0000000
--- a/Podfile
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Podfile
-# Status
-#
-# Created by Pierluigi Galdi on 18/01/2020.
-# Copyright © 2020 Pierluigi Galdi. All rights reserved.
-#
-
-target 'Status' do
- # Comment the next line if you don't want to use dynamic frameworks
- use_frameworks!
-
- # Pods for Status
- pod 'PockKit'
-
-end
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index f2bdb41..392419b
--- a/README.md
+++ b/README.md
@@ -1,6 +1,83 @@
-# Status Widget
-Always keep an eye on system information, like what time is it, or maybe WiFi status or Battery status. Configure Status items visibility from settings.
+# **Status V2** — Pock Widget · Touch Bar GIF + Status Bar
-# Preview
-
+
+> ### **GIF Supporting**
+> Display an animated GIF directly on your MacBook Touch Bar — alongside clock, WiFi, battery, and input source.
+
+A [Pock](https://pock.app) widget that lets you **show any animated GIF on your Touch Bar**. Load it from a local file or a URL, control the size and scaling, and preview it live — all from the widget preferences. Perfect if you want to add a custom animation, a meme, a logo, or any GIF to your Touch Bar.
+
+---
+
+## Features
+
+- **Animated GIF on Touch Bar** — display any `.gif` from a file or URL on the right edge of your Touch Bar
+- **Width control** — resize the GIF from 30 to 300 pt
+- **Scaling modes** — Fit, Fill (crop), or Stretch
+- **Live preview** — see your GIF at real Touch Bar height before applying
+- **Clock** — customizable date & time format
+- **WiFi** — signal strength indicator
+- **Battery** — icon and/or percentage
+- **Input Source** — current keyboard language
+
+---
+
+## Requirements
+
+- MacBook with Touch Bar
+- macOS 10.15 or later
+- [Pock](https://pock.app) installed
+
+---
+
+## Installation
+
+1. Download the latest `.pock` file from [Releases](../../releases/tag/v1.0.0)
+2. Double-click it — Pock will install it automatically
+3. Open Pock → Widgets Manager and enable **Status V2**
+4. Open preferences → enable **Show GIF** → paste a URL or choose a file
+
+---
+
+## Building from Source
+
+No CocoaPods required. The project uses **Swift Package Manager**.
+
+1. Clone the repo
+2. Open `Status.xcodeproj` in Xcode
+3. Xcode will automatically resolve the SPM dependencies ([PockKit](https://github.com/pock/pockkit), [TinyConstraints](https://github.com/roberthein/TinyConstraints))
+4. Build (`⌘B`) — the `.pock` bundle appears in the Products group
+5. Double-click the built `.pock` to install
+
+---
+
+## Preferences
+
+
+
+Open **Pock → Widgets Manager → Status V2** to configure:
+
+| Setting | Description |
+|---|---|
+| Language / Input Source | Toggle the input source indicator |
+| WiFi Signal | Toggle the WiFi signal icon |
+| Battery | Toggle battery display; choose icon and/or percentage |
+| Date & Time | Toggle the clock; set a custom date/time format |
+| **Show GIF** | Enable animated GIF on the right edge of the Touch Bar |
+| Source | Load GIF from a local **file** or a **URL** |
+| Width | Set the GIF display width (30–300 pt) |
+| Scaling | Fit / Fill (crop) / Stretch |
+| Preview | Live preview of your GIF at Touch Bar height |
+
+---
+
+## Keywords
+
+`touch bar gif` · `touchbar gif` · `add gif to touch bar` · `pock widget gif` · `macbook touch bar animation` · `custom touch bar` · `touch bar customization` · `pock gif widget` · `animated touch bar` · `touch bar status widget`
+
+---
+
+## Credits
+
+Based on the original [status-widget](https://github.com/pock/status-widget) by [Pierluigi Galdi](https://github.com/pigigaldi).
+GIF integration and Status V2 by [EvyatarEshkar](https://github.com/EvyatarEshkar).
diff --git a/Status.xcodeproj/project.pbxproj b/Status.xcodeproj/project.pbxproj
old mode 100644
new mode 100755
index 43772c3..481860d
--- a/Status.xcodeproj/project.pbxproj
+++ b/Status.xcodeproj/project.pbxproj
@@ -18,12 +18,13 @@
3401774C23D325DD00369905 /* StatusWidgetPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3401774923D325DD00369905 /* StatusWidgetPreferencePane.swift */; };
34E6A27E2558706B002693EA /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 34E6A27D2558706B002693EA /* Media.xcassets */; };
34E6A281255874FA002693EA /* SLangItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34E6A280255874FA002693EA /* SLangItem.swift */; };
- C1D0E7696EB2EC07C7557D36 /* Pods_Status.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3712FB53353E8D66C89087DD /* Pods_Status.framework */; };
+ AA000005000000050000005A /* SGifItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA000006000000060000006A /* SGifItem.swift */; };
+ AA000003000000030000003C /* PockKit in Frameworks */ = {isa = PBXBuildFile; productRef = AA000001000000010000001A /* PockKit */; };
+ AA000004000000040000004C /* TinyConstraints in Frameworks */ = {isa = PBXBuildFile; productRef = AA000002000000020000002A /* TinyConstraints */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
3401772723D3231D00369905 /* Status.pock */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Status.pock; sourceTree = BUILT_PRODUCTS_DIR; };
- 3401772A23D3231D00369905 /* Podfile */ = {isa = PBXFileReference; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = ""; };
3401772C23D3231D00369905 /* StatusWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusWidget.swift; sourceTree = ""; };
3401772E23D3231D00369905 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
3401773423D323CD00369905 /* StatusItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusItem.swift; sourceTree = ""; };
@@ -40,9 +41,7 @@
3401774923D325DD00369905 /* StatusWidgetPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusWidgetPreferencePane.swift; sourceTree = ""; };
34E6A27D2558706B002693EA /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; };
34E6A280255874FA002693EA /* SLangItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SLangItem.swift; sourceTree = ""; };
- 3712FB53353E8D66C89087DD /* Pods_Status.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Status.framework; sourceTree = BUILT_PRODUCTS_DIR; };
- 9347816283C4AB2AF67D68D4 /* Pods-Status.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Status.release.xcconfig"; path = "Target Support Files/Pods-Status/Pods-Status.release.xcconfig"; sourceTree = ""; };
- CB926D07BCC33E6C4950E6CD /* Pods-Status.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Status.debug.xcconfig"; path = "Target Support Files/Pods-Status/Pods-Status.debug.xcconfig"; sourceTree = ""; };
+ AA000006000000060000006A /* SGifItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SGifItem.swift; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -50,7 +49,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- C1D0E7696EB2EC07C7557D36 /* Pods_Status.framework in Frameworks */,
+ AA000003000000030000003C /* PockKit in Frameworks */,
+ AA000004000000040000004C /* TinyConstraints in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -62,8 +62,6 @@
children = (
3401772923D3231D00369905 /* Status */,
3401772823D3231D00369905 /* Products */,
- 43D9A762481AC806E3D29B23 /* Pods */,
- E407CFE0DC443983050B23C8 /* Frameworks */,
);
sourceTree = "";
};
@@ -78,7 +76,6 @@
3401772923D3231D00369905 /* Status */ = {
isa = PBXGroup;
children = (
- 3401772A23D3231D00369905 /* Podfile */,
3401772E23D3231D00369905 /* Info.plist */,
3401772B23D3231D00369905 /* Sources */,
);
@@ -101,6 +98,7 @@
isa = PBXGroup;
children = (
3401773623D3241000369905 /* SClockItem.swift */,
+ AA000006000000060000006A /* SGifItem.swift */,
34E6A280255874FA002693EA /* SLangItem.swift */,
3401773723D3241000369905 /* SPowerItem.swift */,
3401773823D3241000369905 /* SWifiItem.swift */,
@@ -127,23 +125,6 @@
path = StatusWidgetPreferencePane;
sourceTree = "";
};
- 43D9A762481AC806E3D29B23 /* Pods */ = {
- isa = PBXGroup;
- children = (
- CB926D07BCC33E6C4950E6CD /* Pods-Status.debug.xcconfig */,
- 9347816283C4AB2AF67D68D4 /* Pods-Status.release.xcconfig */,
- );
- path = Pods;
- sourceTree = "";
- };
- E407CFE0DC443983050B23C8 /* Frameworks */ = {
- isa = PBXGroup;
- children = (
- 3712FB53353E8D66C89087DD /* Pods_Status.framework */,
- );
- name = Frameworks;
- sourceTree = "";
- };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -151,7 +132,6 @@
isa = PBXNativeTarget;
buildConfigurationList = 3401773123D3231D00369905 /* Build configuration list for PBXNativeTarget "Status" */;
buildPhases = (
- 1BC8629D672E307759B16C56 /* [CP] Check Pods Manifest.lock */,
3401772223D3231D00369905 /* Sources */,
3401772323D3231D00369905 /* Frameworks */,
3401772423D3231D00369905 /* Resources */,
@@ -162,6 +142,10 @@
dependencies = (
);
name = Status;
+ packageProductDependencies = (
+ AA000001000000010000001A /* PockKit */,
+ AA000002000000020000002A /* TinyConstraints */,
+ );
productName = Status;
productReference = 3401772723D3231D00369905 /* Status.pock */;
productType = "com.apple.product-type.bundle";
@@ -194,6 +178,10 @@
tr,
);
mainGroup = 3401771D23D3231D00369905;
+ packageReferences = (
+ BB000001000000010000001B /* XCRemoteSwiftPackageReference "pockkit" */,
+ BB000002000000020000002B /* XCRemoteSwiftPackageReference "TinyConstraints" */,
+ );
productRefGroup = 3401772823D3231D00369905 /* Products */;
projectDirPath = "";
projectRoot = "";
@@ -208,37 +196,13 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 3401774B23D325DD00369905 /* StatusWidgetPreferencePane.xib in Resources */,
34E6A27E2558706B002693EA /* Media.xcassets in Resources */,
- 3401774A23D325DD00369905 /* StatusWidgetPreferencePane.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 1BC8629D672E307759B16C56 /* [CP] Check Pods Manifest.lock */ = {
- isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
- files = (
- );
- inputFileListPaths = (
- );
- inputPaths = (
- "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
- "${PODS_ROOT}/Manifest.lock",
- );
- name = "[CP] Check Pods Manifest.lock";
- outputFileListPaths = (
- );
- outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Status-checkManifestLockResult.txt",
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
- showEnvVarsInLog = 0;
- };
3401772523D3231D00369905 /* Copy widget to destination folder */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -264,6 +228,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ AA000005000000050000005A /* SGifItem.swift in Sources */,
3401773923D3241000369905 /* SClockItem.swift in Sources */,
3401773523D323CD00369905 /* StatusItem.swift in Sources */,
3401774C23D325DD00369905 /* StatusWidgetPreferencePane.swift in Sources */,
@@ -351,7 +316,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
@@ -403,7 +368,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.13;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
@@ -412,19 +377,16 @@
};
3401773223D3231D00369905 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = CB926D07BCC33E6C4950E6CD /* Pods-Status.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
- CODE_SIGN_IDENTITY = "Developer ID Application";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = 788D9WZ9Z3;
INFOPLIST_FILE = Status/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Application\\ Support/Pock/Widgets";
MARKETING_VERSION = 1.1;
- PRODUCT_BUNDLE_IDENTIFIER = com.pigigaldi.pock.Status;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "";
+ PRODUCT_BUNDLE_IDENTIFIER = com.evyatareshkar.pock.StatusV2;
+ PRODUCT_MODULE_NAME = Status;
+ PRODUCT_NAME = StatusV2;
SWIFT_VERSION = 5.0;
WRAPPER_EXTENSION = pock;
};
@@ -432,19 +394,16 @@
};
3401773323D3231D00369905 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 9347816283C4AB2AF67D68D4 /* Pods-Status.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
- CODE_SIGN_IDENTITY = "Developer ID Application";
- CODE_SIGN_STYLE = Manual;
+ CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- DEVELOPMENT_TEAM = 788D9WZ9Z3;
INFOPLIST_FILE = Status/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Application\\ Support/Pock/Widgets";
MARKETING_VERSION = 1.1;
- PRODUCT_BUNDLE_IDENTIFIER = com.pigigaldi.pock.Status;
- PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "";
+ PRODUCT_BUNDLE_IDENTIFIER = com.evyatareshkar.pock.StatusV2;
+ PRODUCT_MODULE_NAME = Status;
+ PRODUCT_NAME = StatusV2;
SWIFT_VERSION = 5.0;
WRAPPER_EXTENSION = pock;
};
@@ -472,6 +431,38 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ BB000001000000010000001B /* XCRemoteSwiftPackageReference "pockkit" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/pock/pockkit.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 0.3.1;
+ };
+ };
+ BB000002000000020000002B /* XCRemoteSwiftPackageReference "TinyConstraints" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/roberthein/TinyConstraints.git";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 4.0.2;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ AA000001000000010000001A /* PockKit */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = BB000001000000010000001B /* XCRemoteSwiftPackageReference "pockkit" */;
+ productName = PockKit;
+ };
+ AA000002000000020000002A /* TinyConstraints */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = BB000002000000020000002B /* XCRemoteSwiftPackageReference "TinyConstraints" */;
+ productName = TinyConstraints;
+ };
+/* End XCSwiftPackageProductDependency section */
};
rootObject = 3401771E23D3231D00369905 /* Project object */;
}
diff --git a/Status.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Status.xcodeproj/project.xcworkspace/contents.xcworkspacedata
old mode 100644
new mode 100755
diff --git a/Status.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Status.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
old mode 100644
new mode 100755
diff --git a/Status.xcodeproj/xcuserdata/Pierluigi.xcuserdatad/xcschemes/xcschememanagement.plist b/Status.xcodeproj/xcuserdata/Pierluigi.xcuserdatad/xcschemes/xcschememanagement.plist
deleted file mode 100644
index 02d34ce..0000000
--- a/Status.xcodeproj/xcuserdata/Pierluigi.xcuserdatad/xcschemes/xcschememanagement.plist
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
- SchemeUserState
-
- Status.xcscheme_^#shared#^_
-
- orderHint
- 3
-
-
-
-
diff --git a/Status.xcworkspace/contents.xcworkspacedata b/Status.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index dd7380a..0000000
--- a/Status.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
diff --git a/Status.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Status.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
deleted file mode 100644
index 18d9810..0000000
--- a/Status.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- IDEDidComputeMac32BitWarning
-
-
-
diff --git a/Status/Info.plist b/Status/Info.plist
old mode 100644
new mode 100755
index 36479d2..113fc7f
--- a/Status/Info.plist
+++ b/Status/Info.plist
@@ -11,7 +11,7 @@
CFBundleInfoDictionaryVersion
6.0
CFBundleName
- $(PRODUCT_NAME)
+ Status V2
CFBundlePackageType
BNDL
CFBundleShortVersionString
@@ -21,10 +21,10 @@
NSHumanReadableCopyright
Copyright © 2020 Pierluigi Galdi. All rights reserved.
NSPrincipalClass
- Status.StatusWidget
+ Status.StatusV2Widget
PKWidgetAuthor
- Pierluigi Galdi
+ EvyatarEshkar
PKWidgetPreferenceClass
- Status.StatusWidgetPreferencePane
+ Status.StatusV2PreferencePane
diff --git a/Status/Sources/Items/SClockItem.swift b/Status/Sources/Items/SClockItem.swift
old mode 100644
new mode 100755
diff --git a/Status/Sources/Items/SGifItem.swift b/Status/Sources/Items/SGifItem.swift
new file mode 100644
index 0000000..11b0bea
--- /dev/null
+++ b/Status/Sources/Items/SGifItem.swift
@@ -0,0 +1,113 @@
+//
+// SGifItem.swift
+// Status
+//
+
+import Foundation
+import AppKit
+
+internal class SGifItem: StatusItem {
+
+ // MARK: - StatusItem
+
+ var enabled: Bool { return Preferences[.shouldShowGif] }
+ var title: String { return "gif" }
+ var view: NSView { return imageView }
+
+ func action() { }
+
+ func didLoad() {
+ applyDimensions()
+ applyScaling()
+ reload()
+ }
+
+ func didUnload() {
+ imageView.image = nil
+ }
+
+ // MARK: - UI
+
+ private let imageView: NSImageView = {
+ let iv = NSImageView()
+ iv.translatesAutoresizingMaskIntoConstraints = false
+ iv.animates = true
+ iv.wantsLayer = true
+ iv.layer?.cornerRadius = 4
+ iv.layer?.masksToBounds = true
+ return iv
+ }()
+
+ private var widthConstraint: NSLayoutConstraint?
+ private var heightConstraint: NSLayoutConstraint?
+
+ init() {
+ widthConstraint = imageView.widthAnchor.constraint(equalToConstant: 60)
+ heightConstraint = imageView.heightAnchor.constraint(equalToConstant: 30)
+ widthConstraint?.isActive = true
+ heightConstraint?.isActive = true
+ didLoad()
+ }
+
+ deinit { didUnload() }
+
+ // MARK: - Reload
+
+ func reload() {
+ applyDimensions()
+ applyScaling()
+
+ let sourceType: Int = Preferences[.gifSourceType]
+ if sourceType == 0 {
+ let path: String = Preferences[.gifFilePath]
+ guard !path.isEmpty else { return }
+ loadFromFile(path)
+ } else {
+ let urlStr: String = Preferences[.gifURLString]
+ guard !urlStr.isEmpty, let url = URL(string: urlStr) else { return }
+ loadFromURL(url)
+ }
+ }
+
+ // MARK: - Helpers
+
+ private func applyDimensions() {
+ let w: Double = Preferences[.gifWidth]
+ widthConstraint?.constant = CGFloat(w)
+ }
+
+ private func applyScaling() {
+ let mode: Int = Preferences[.gifScalingMode]
+ switch mode {
+ case 1:
+ imageView.imageScaling = .scaleAxesIndependently
+ imageView.layer?.contentsGravity = .resizeAspectFill
+ case 2:
+ imageView.imageScaling = .scaleAxesIndependently
+ imageView.layer?.contentsGravity = .resize
+ default:
+ imageView.imageScaling = .scaleProportionallyUpOrDown
+ imageView.layer?.contentsGravity = .resizeAspect
+ }
+ }
+
+ private func loadFromFile(_ path: String) {
+ guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)),
+ let image = NSImage(data: data) else { return }
+ DispatchQueue.main.async { [weak self] in
+ self?.imageView.image = image
+ self?.imageView.animates = true
+ }
+ }
+
+ private func loadFromURL(_ url: URL) {
+ let req = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 30)
+ URLSession.shared.dataTask(with: req) { [weak self] data, _, _ in
+ guard let data, let image = NSImage(data: data) else { return }
+ DispatchQueue.main.async {
+ self?.imageView.image = image
+ self?.imageView.animates = true
+ }
+ }.resume()
+ }
+}
diff --git a/Status/Sources/Items/SLangItem.swift b/Status/Sources/Items/SLangItem.swift
old mode 100644
new mode 100755
diff --git a/Status/Sources/Items/SPowerItem.swift b/Status/Sources/Items/SPowerItem.swift
old mode 100644
new mode 100755
index e6f8b50..b37b9aa
--- a/Status/Sources/Items/SPowerItem.swift
+++ b/Status/Sources/Items/SPowerItem.swift
@@ -112,7 +112,7 @@ internal class SPowerItem: StatusItem {
iconName = "powerEmpty"
buildBatteryIcon(withValue: value)
}
- iconView.image = Bundle(for: StatusWidget.self).image(forResource: iconName)
+ iconView.image = Bundle(for: StatusV2Widget.self).image(forResource: iconName)
iconView.isHidden = false
}else {
iconView.isHidden = true
diff --git a/Status/Sources/Items/SWifiItem.swift b/Status/Sources/Items/SWifiItem.swift
old mode 100644
new mode 100755
index bc58af6..dd9aeb7
--- a/Status/Sources/Items/SWifiItem.swift
+++ b/Status/Sources/Items/SWifiItem.swift
@@ -64,7 +64,7 @@ internal class SWifiItem: StatusItem {
let c = code - 1
icon = "wifi\(c > 4 ? 4 : c)"
}
- self.iconView.image = Bundle(for: StatusWidget.self).image(forResource: icon)
+ self.iconView.image = Bundle(for: StatusV2Widget.self).image(forResource: icon)
}
}
diff --git a/Status/Sources/Media.xcassets/Contents.json b/Status/Sources/Media.xcassets/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerEmpty.imageset/BatteryEmpty.pdf b/Status/Sources/Media.xcassets/powerEmpty.imageset/BatteryEmpty.pdf
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerEmpty.imageset/Contents.json b/Status/Sources/Media.xcassets/powerEmpty.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerIsCharged.imageset/BatteryCharged.pdf b/Status/Sources/Media.xcassets/powerIsCharged.imageset/BatteryCharged.pdf
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerIsCharged.imageset/Contents.json b/Status/Sources/Media.xcassets/powerIsCharged.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerIsCharging.imageset/BatteryCharging-1.pdf b/Status/Sources/Media.xcassets/powerIsCharging.imageset/BatteryCharging-1.pdf
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerIsCharging.imageset/Contents.json b/Status/Sources/Media.xcassets/powerIsCharging.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerLeft.imageset/BatteryLevelCapB-L.pdf b/Status/Sources/Media.xcassets/powerLeft.imageset/BatteryLevelCapB-L.pdf
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerLeft.imageset/Contents.json b/Status/Sources/Media.xcassets/powerLeft.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerMiddle.imageset/BatteryLevelCapB-M.pdf b/Status/Sources/Media.xcassets/powerMiddle.imageset/BatteryLevelCapB-M.pdf
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerMiddle.imageset/Contents.json b/Status/Sources/Media.xcassets/powerMiddle.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerRight.imageset/BatteryLevelCapB-R.pdf b/Status/Sources/Media.xcassets/powerRight.imageset/BatteryLevelCapB-R.pdf
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/powerRight.imageset/Contents.json b/Status/Sources/Media.xcassets/powerRight.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi0.imageset/AirPort0.png b/Status/Sources/Media.xcassets/wifi0.imageset/AirPort0.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi0.imageset/AirPort0@2x.png b/Status/Sources/Media.xcassets/wifi0.imageset/AirPort0@2x.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi0.imageset/Contents.json b/Status/Sources/Media.xcassets/wifi0.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi1.imageset/AirPort1.png b/Status/Sources/Media.xcassets/wifi1.imageset/AirPort1.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi1.imageset/AirPort1@2x.png b/Status/Sources/Media.xcassets/wifi1.imageset/AirPort1@2x.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi1.imageset/Contents.json b/Status/Sources/Media.xcassets/wifi1.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi2.imageset/AirPort2.png b/Status/Sources/Media.xcassets/wifi2.imageset/AirPort2.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi2.imageset/AirPort2@2x.png b/Status/Sources/Media.xcassets/wifi2.imageset/AirPort2@2x.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi2.imageset/Contents.json b/Status/Sources/Media.xcassets/wifi2.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi3.imageset/AirPort3.png b/Status/Sources/Media.xcassets/wifi3.imageset/AirPort3.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi3.imageset/AirPort3@2x.png b/Status/Sources/Media.xcassets/wifi3.imageset/AirPort3@2x.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi3.imageset/Contents.json b/Status/Sources/Media.xcassets/wifi3.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi4.imageset/AirPort4.png b/Status/Sources/Media.xcassets/wifi4.imageset/AirPort4.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi4.imageset/AirPort4@2x.png b/Status/Sources/Media.xcassets/wifi4.imageset/AirPort4@2x.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifi4.imageset/Contents.json b/Status/Sources/Media.xcassets/wifi4.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifiOff.imageset/AirPortOff.png b/Status/Sources/Media.xcassets/wifiOff.imageset/AirPortOff.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifiOff.imageset/AirPortOff@2x.png b/Status/Sources/Media.xcassets/wifiOff.imageset/AirPortOff@2x.png
old mode 100644
new mode 100755
diff --git a/Status/Sources/Media.xcassets/wifiOff.imageset/Contents.json b/Status/Sources/Media.xcassets/wifiOff.imageset/Contents.json
old mode 100644
new mode 100755
diff --git a/Status/Sources/Preferences/Preferences.swift b/Status/Sources/Preferences/Preferences.swift
old mode 100644
new mode 100755
index 879b622..686c6e0
--- a/Status/Sources/Preferences/Preferences.swift
+++ b/Status/Sources/Preferences/Preferences.swift
@@ -14,6 +14,7 @@ extension NSNotification.Name {
internal struct Preferences {
internal enum Keys: String {
+ // Status items
case shouldShowLangItem
case shouldShowWifiItem
case shouldShowPowerItem
@@ -21,25 +22,32 @@ internal struct Preferences {
case shouldShowBatteryPercentage
case shouldShowDateItem
case timeFormatTextField
+ // GIF
+ case shouldShowGif
+ case gifSourceType // Int — 0 = file, 1 = url
+ case gifFilePath // String
+ case gifURLString // String
+ case gifWidth // Double
+ case gifScalingMode // Int — 0 = fit, 1 = fill, 2 = stretch
}
+
static subscript(_ key: Keys) -> T {
get {
guard let value = UserDefaults.standard.value(forKey: key.rawValue) as? T else {
switch key {
- case .shouldShowLangItem:
- return false as! T
- case .shouldShowWifiItem:
- return true as! T
- case .shouldShowPowerItem:
- return true as! T
- case .shouldShowBatteryIcon:
- return true as! T
- case .shouldShowBatteryPercentage:
- return false as! T
- case .shouldShowDateItem:
- return true as! T
- case .timeFormatTextField:
- return "EE dd MMM HH:mm" as! T
+ case .shouldShowLangItem: return false as! T
+ case .shouldShowWifiItem: return true as! T
+ case .shouldShowPowerItem: return true as! T
+ case .shouldShowBatteryIcon: return true as! T
+ case .shouldShowBatteryPercentage: return false as! T
+ case .shouldShowDateItem: return true as! T
+ case .timeFormatTextField: return "EE dd MMM HH:mm" as! T
+ case .shouldShowGif: return false as! T
+ case .gifSourceType: return 1 as! T // URL
+ case .gifFilePath: return "" as! T
+ case .gifURLString: return "" as! T
+ case .gifWidth: return 60.0 as! T
+ case .gifScalingMode: return 0 as! T // fit
}
}
return value
@@ -48,13 +56,20 @@ internal struct Preferences {
UserDefaults.standard.setValue(newValue, forKey: key.rawValue)
}
}
+
static func reset() {
- Preferences[.shouldShowLangItem] = false
- Preferences[.shouldShowWifiItem] = true
- Preferences[.shouldShowPowerItem] = true
- Preferences[.shouldShowBatteryIcon] = true
- Preferences[.shouldShowBatteryPercentage] = true
- Preferences[.shouldShowDateItem] = true
- Preferences[.timeFormatTextField] = "EE dd MMM HH:mm"
+ Preferences[.shouldShowLangItem] = false
+ Preferences[.shouldShowWifiItem] = true
+ Preferences[.shouldShowPowerItem] = true
+ Preferences[.shouldShowBatteryIcon] = true
+ Preferences[.shouldShowBatteryPercentage] = false
+ Preferences[.shouldShowDateItem] = true
+ Preferences[.timeFormatTextField] = "EE dd MMM HH:mm"
+ Preferences[.shouldShowGif] = false
+ Preferences[.gifSourceType] = 1
+ Preferences[.gifFilePath] = ""
+ Preferences[.gifURLString] = ""
+ Preferences[.gifWidth] = 60.0
+ Preferences[.gifScalingMode] = 0
}
}
diff --git a/Status/Sources/Preferences/StatusWidgetPreferencePane/Base.lproj/StatusWidgetPreferencePane.xib b/Status/Sources/Preferences/StatusWidgetPreferencePane/Base.lproj/StatusWidgetPreferencePane.xib
deleted file mode 100644
index 5c98a2f..0000000
--- a/Status/Sources/Preferences/StatusWidgetPreferencePane/Base.lproj/StatusWidgetPreferencePane.xib
+++ /dev/null
@@ -1,172 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Status/Sources/Preferences/StatusWidgetPreferencePane/StatusWidgetPreferencePane.swift b/Status/Sources/Preferences/StatusWidgetPreferencePane/StatusWidgetPreferencePane.swift
old mode 100644
new mode 100755
index 40e1724..131196d
--- a/Status/Sources/Preferences/StatusWidgetPreferencePane/StatusWidgetPreferencePane.swift
+++ b/Status/Sources/Preferences/StatusWidgetPreferencePane/StatusWidgetPreferencePane.swift
@@ -1,79 +1,411 @@
//
// StatusWidgetPreferencePane.swift
-// Pock
+// Status
//
-// Created by Pierluigi Galdi on 30/03/2019.
-// Copyright © 2019 Pierluigi Galdi. All rights reserved.
+// Programmatic rewrite — drops XIB, adds GIF settings section.
//
import Cocoa
import PockKit
+import UniformTypeIdentifiers
+
+// Named differently from any XIB file to prevent nib auto-loading
+class StatusV2PreferencePane: NSViewController, NSTextFieldDelegate, PKWidgetPreference {
+
+ static var nibName: NSNib.Name = NSNib.Name("")
+
+ // MARK: Status controls
+ private weak var showLangCheck: NSButton!
+ private weak var showWifiCheck: NSButton!
+ private weak var showPowerCheck: NSButton!
+ private weak var showBatteryIconCheck: NSButton!
+ private weak var showBatteryPctCheck: NSButton!
+ private weak var showDateCheck: NSButton!
+ private weak var timeFormatField: NSTextField!
+
+ // MARK: GIF controls
+ private weak var showGifCheck: NSButton!
+ private weak var gifContentStack: NSView!
+ private weak var gifSourceControl: NSSegmentedControl!
+ private weak var gifURLRow: NSView!
+ private weak var gifURLField: NSTextField!
+ private weak var gifFileRow: NSView!
+ private weak var gifFileField: NSTextField!
+ private weak var gifWidthSlider: NSSlider!
+ private weak var gifWidthLabel: NSTextField!
+ private weak var gifScalePopup: NSPopUpButton!
+ private weak var gifPreviewImageView: NSImageView!
+ private weak var gifPreviewWidthConstraint: NSLayoutConstraint!
+
+ // MARK: - Init
+
+ override init(nibName: NSNib.Name?, bundle: Bundle?) {
+ super.init(nibName: nil, bundle: nil)
+ }
+ required init?(coder: NSCoder) { super.init(coder: coder) }
+
+ override func loadView() {
+ self.view = buildUI()
+ }
-class StatusWidgetPreferencePane: NSViewController, NSTextFieldDelegate, PKWidgetPreference {
-
- static var nibName: NSNib.Name = "StatusWidgetPreferencePane"
-
- /// UI
- @IBOutlet weak var showLangItem: NSButton!
- @IBOutlet weak var showWifiItem: NSButton!
- @IBOutlet weak var showPowerItem: NSButton!
- @IBOutlet weak var showBatteryIconItem: NSButton!
- @IBOutlet weak var showBatteryPercentageItem: NSButton!
- @IBOutlet weak var showDateItem: NSButton!
- @IBOutlet weak var timeFormatTextField: NSTextField!
-
func reset() {
Preferences.reset()
- loadCheckboxState()
NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
+ self.view = buildUI()
}
-
- override func viewDidLoad() {
- super.viewDidLoad()
- self.view.superview?.wantsLayer = true
- self.view.wantsLayer = true
- self.loadCheckboxState()
- self.timeFormatTextField.delegate = self
- self.timeFormatTextField.stringValue = Preferences[.timeFormatTextField]
- }
-
- private func loadCheckboxState() {
- self.showLangItem.state = Preferences[.shouldShowLangItem] ? .on : .off
- self.showWifiItem.state = Preferences[.shouldShowWifiItem] ? .on : .off
- self.showPowerItem.state = Preferences[.shouldShowPowerItem] ? .on : .off
- self.showBatteryIconItem.state = Preferences[.shouldShowBatteryIcon] ? .on : .off
- self.showBatteryPercentageItem.state = Preferences[.shouldShowBatteryPercentage] ? .on : .off
- self.showDateItem.state = Preferences[.shouldShowDateItem] ? .on : .off
- }
-
- @IBAction func didChangeCheckboxValue(_ checkbox: NSButton) {
- var key: Preferences.Keys
- switch checkbox.tag {
- case 0:
- key = .shouldShowLangItem
- case 1:
- key = .shouldShowWifiItem
- case 2:
- key = .shouldShowPowerItem
- case 21:
- key = .shouldShowBatteryIcon
- case 22:
- key = .shouldShowBatteryPercentage
- case 3:
- key = .shouldShowDateItem
- default:
- return
+
+ // MARK: - Build UI
+
+ private func buildUI() -> NSView {
+ let root = NSStackView()
+ root.orientation = .vertical
+ root.alignment = .leading
+ root.spacing = 5
+ root.edgeInsets = NSEdgeInsets(top: 14, left: 16, bottom: 14, right: 16)
+ root.translatesAutoresizingMaskIntoConstraints = false
+ root.widthAnchor.constraint(greaterThanOrEqualToConstant: 340).isActive = true
+
+ // ── STATUS ITEMS ─────────────────────────────────────────────────────
+ root.addArrangedSubview(sectionLabel("Status Items"))
+
+ let langCk = makeCheckbox("Language / Input Source", tag: 0,
+ on: Preferences[.shouldShowLangItem])
+ showLangCheck = langCk
+ root.addArrangedSubview(langCk)
+
+ let wifiCk = makeCheckbox("WiFi Signal", tag: 1,
+ on: Preferences[.shouldShowWifiItem])
+ showWifiCheck = wifiCk
+ root.addArrangedSubview(wifiCk)
+
+ let powerCk = makeCheckbox("Battery", tag: 2,
+ on: Preferences[.shouldShowPowerItem])
+ showPowerCheck = powerCk
+ root.addArrangedSubview(powerCk)
+
+ // Battery sub-options (indented)
+ let battSub = NSStackView()
+ battSub.orientation = .vertical
+ battSub.alignment = .leading
+ battSub.spacing = 3
+ battSub.edgeInsets.left = 18
+ let iconCk = makeCheckbox("Show icon", tag: 21,
+ on: Preferences[.shouldShowBatteryIcon])
+ showBatteryIconCheck = iconCk
+ let pctCk = makeCheckbox("Show percentage", tag: 22,
+ on: Preferences[.shouldShowBatteryPercentage])
+ showBatteryPctCheck = pctCk
+ battSub.addArrangedSubview(iconCk)
+ battSub.addArrangedSubview(pctCk)
+ root.addArrangedSubview(battSub)
+
+ let dateCk = makeCheckbox("Date & Time", tag: 3,
+ on: Preferences[.shouldShowDateItem])
+ showDateCheck = dateCk
+ root.addArrangedSubview(dateCk)
+
+ // Time format
+ let tf = NSTextField()
+ tf.stringValue = Preferences[.timeFormatTextField]
+ tf.placeholderString = "EE dd MMM HH:mm"
+ tf.delegate = self
+ timeFormatField = tf
+
+ let helpBtn = NSButton(title: "", target: self, action: #selector(openTimeFormatHelp))
+ helpBtn.bezelStyle = .helpButton
+
+ let tfInner = NSStackView(views: [tf, helpBtn])
+ tfInner.spacing = 6
+ let tfRow = makeRow("Format:", tfInner)
+ tfRow.edgeInsets.left = 18
+ root.addArrangedSubview(tfRow)
+
+ // ── SEPARATOR ────────────────────────────────────────────────────────
+ root.addArrangedSubview(makeSeparator())
+
+ // ── GIF ──────────────────────────────────────────────────────────────
+ root.addArrangedSubview(sectionLabel("GIF"))
+
+ let shouldShowGif: Bool = Preferences[.shouldShowGif]
+ let gifToggle = NSButton(checkboxWithTitle: "Show GIF on right edge",
+ target: self, action: #selector(gifToggleChanged))
+ gifToggle.state = shouldShowGif ? .on : .off
+ showGifCheck = gifToggle
+ root.addArrangedSubview(gifToggle)
+
+ // GIF content stack — shown only when toggle is on
+ let gifStack = NSStackView()
+ gifStack.orientation = .vertical
+ gifStack.alignment = .leading
+ gifStack.spacing = 5
+ gifStack.edgeInsets.left = 8
+ gifStack.isHidden = !shouldShowGif
+ gifContentStack = gifStack
+
+ // Source selector: File | URL
+ let sourceCtrl = NSSegmentedControl(
+ labels: ["File", "URL"],
+ trackingMode: .selectOne,
+ target: self,
+ action: #selector(gifSourceChanged)
+ )
+ let savedSource: Int = Preferences[.gifSourceType]
+ sourceCtrl.selectedSegment = savedSource
+ gifSourceControl = sourceCtrl
+ gifStack.addArrangedSubview(makeRow("Source:", sourceCtrl))
+
+ // URL row
+ let urlF = NSTextField()
+ let savedURLStr: String = Preferences[.gifURLString]
+ urlF.stringValue = savedURLStr
+ urlF.placeholderString = "https://example.com/animation.gif"
+ urlF.target = self
+ urlF.action = #selector(gifURLChanged)
+ gifURLField = urlF
+ let urlRow = makeRow("URL:", urlF)
+ gifURLRow = urlRow
+ gifStack.addArrangedSubview(urlRow)
+
+ // File row
+ let fileF = NSTextField()
+ let savedPath: String = Preferences[.gifFilePath]
+ fileF.stringValue = savedPath
+ fileF.placeholderString = "No file selected"
+ fileF.isEditable = false
+ fileF.isSelectable = false
+ gifFileField = fileF
+
+ let chooseBtn = NSButton(title: "Choose…", target: self, action: #selector(gifChooseFile))
+ let fileInner = NSStackView(views: [fileF, chooseBtn])
+ fileInner.spacing = 6
+ let fileRow = makeRow("File:", fileInner)
+ gifFileRow = fileRow
+ gifStack.addArrangedSubview(fileRow)
+
+ // Width slider
+ let savedW: Double = Preferences[.gifWidth]
+ let wSlider = NSSlider(value: savedW, minValue: 30, maxValue: 300,
+ target: self, action: #selector(gifWidthChanged))
+ wSlider.controlSize = .small
+ gifWidthSlider = wSlider
+ let wLabel = NSTextField(labelWithString: "\(Int(savedW)) pt")
+ wLabel.font = NSFont.monospacedDigitSystemFont(ofSize: 11, weight: .regular)
+ gifWidthLabel = wLabel
+ let wInner = NSStackView(views: [wSlider, wLabel])
+ wInner.spacing = 6
+ gifStack.addArrangedSubview(makeRow("Width:", wInner))
+
+ // Scaling popup
+ let scaleP = NSPopUpButton()
+ scaleP.addItems(withTitles: ["Fit", "Fill (crop)", "Stretch"])
+ let savedScale: Int = Preferences[.gifScalingMode]
+ scaleP.selectItem(at: savedScale)
+ scaleP.target = self
+ scaleP.action = #selector(gifScalingChanged)
+ gifScalePopup = scaleP
+ gifStack.addArrangedSubview(makeRow("Scaling:", scaleP))
+
+ // Preview (Touch Bar height)
+ let piv = NSImageView()
+ piv.wantsLayer = true
+ piv.layer?.cornerRadius = 4
+ piv.layer?.masksToBounds = true
+ piv.imageScaling = .scaleProportionallyUpOrDown
+ piv.animates = true
+ piv.translatesAutoresizingMaskIntoConstraints = false
+ gifPreviewImageView = piv
+
+ let previewBox = NSView()
+ previewBox.translatesAutoresizingMaskIntoConstraints = false
+ previewBox.wantsLayer = true
+ previewBox.layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor
+ previewBox.layer?.cornerRadius = 4
+ previewBox.addSubview(piv)
+
+ let pwc = previewBox.widthAnchor.constraint(equalToConstant: CGFloat(savedW))
+ gifPreviewWidthConstraint = pwc
+ NSLayoutConstraint.activate([
+ piv.leadingAnchor.constraint(equalTo: previewBox.leadingAnchor),
+ piv.trailingAnchor.constraint(equalTo: previewBox.trailingAnchor),
+ piv.topAnchor.constraint(equalTo: previewBox.topAnchor),
+ piv.bottomAnchor.constraint(equalTo: previewBox.bottomAnchor),
+ previewBox.heightAnchor.constraint(equalToConstant: 30),
+ pwc
+ ])
+ gifStack.addArrangedSubview(makeRow("Preview:", previewBox))
+
+ root.addArrangedSubview(gifStack)
+
+ // Apply initial source row visibility and preview
+ refreshGIFSourceRows()
+ refreshGIFPreview()
+
+ return root
+ }
+
+ // MARK: - Status Actions
+
+ @objc private func checkboxChanged(_ sender: NSButton) {
+ let key: Preferences.Keys
+ switch sender.tag {
+ case 0: key = .shouldShowLangItem
+ case 1: key = .shouldShowWifiItem
+ case 2: key = .shouldShowPowerItem
+ case 21: key = .shouldShowBatteryIcon
+ case 22: key = .shouldShowBatteryPercentage
+ case 3: key = .shouldShowDateItem
+ default: return
}
- Preferences[key] = checkbox.state == .on
- NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
+ Preferences[key] = sender.state == .on
+ NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
}
-
- @IBAction func openTimeFormatHelpURL(_ sender: NSButton) {
+
+ @objc private func openTimeFormatHelp() {
guard let url = URL(string: "https://www.mowglii.com/itsycal/datetime.html") else { return }
NSWorkspace.shared.open(url)
}
-
+
+ // Fires on every keystroke — SClockItem's 1-second timer picks it up automatically
func controlTextDidChange(_ obj: Notification) {
- Preferences[.timeFormatTextField] = timeFormatTextField.stringValue
+ guard let field = obj.object as? NSTextField, field === timeFormatField else { return }
+ Preferences[.timeFormatTextField] = field.stringValue
+ }
+
+ // MARK: - GIF Actions
+
+ @objc private func gifToggleChanged() {
+ let on = showGifCheck.state == .on
+ Preferences[.shouldShowGif] = on
+ gifContentStack.isHidden = !on
+ NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
+ }
+
+ @objc private func gifSourceChanged() {
+ Preferences[.gifSourceType] = gifSourceControl.selectedSegment
+ refreshGIFSourceRows()
+ refreshGIFPreview()
+ NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
+ }
+
+ @objc private func gifURLChanged() {
+ let text = gifURLField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
+ Preferences[.gifURLString] = text
+ refreshGIFPreview()
+ NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
+ }
+
+ @objc private func gifChooseFile() {
+ let panel = NSOpenPanel()
+ if #available(macOS 12.0, *) {
+ panel.allowedContentTypes = [UTType.gif]
+ } else {
+ panel.allowedFileTypes = ["gif"]
+ }
+ panel.allowsMultipleSelection = false
+ panel.canChooseDirectories = false
+ panel.begin { [weak self] result in
+ guard let self, result == .OK, let url = panel.url else { return }
+ Preferences[.gifFilePath] = url.path
+ self.gifFileField.stringValue = url.path
+ self.refreshGIFPreview()
+ NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
+ }
+ }
+
+ @objc private func gifWidthChanged() {
+ let w = gifWidthSlider.doubleValue
+ Preferences[.gifWidth] = w
+ gifWidthLabel.stringValue = "\(Int(w)) pt"
+ gifPreviewWidthConstraint.constant = CGFloat(w)
+ NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
+ }
+
+ @objc private func gifScalingChanged() {
+ let mode = gifScalePopup.indexOfSelectedItem
+ Preferences[.gifScalingMode] = mode
+ if let piv = gifPreviewImageView { applyScaling(mode, to: piv) }
+ NotificationCenter.default.post(name: .shouldReloadStatusWidget, object: nil)
+ }
+
+ // MARK: - GIF Helpers
+
+ private func refreshGIFSourceRows() {
+ let isURL = gifSourceControl.selectedSegment == 1
+ gifURLRow.isHidden = !isURL
+ gifFileRow.isHidden = isURL
+ }
+
+ private func refreshGIFPreview() {
+ guard let piv = gifPreviewImageView else { return }
+ let isURL = gifSourceControl.selectedSegment == 1
+ let mode = gifScalePopup.indexOfSelectedItem
+
+ if isURL {
+ let str: String = Preferences[.gifURLString]
+ guard let url = URL(string: str) else { return }
+ URLSession.shared.dataTask(with: url) { [weak self, weak piv] data, _, _ in
+ guard let self, let piv, let data, let img = NSImage(data: data) else { return }
+ DispatchQueue.main.async {
+ piv.image = img
+ piv.animates = true
+ self.applyScaling(mode, to: piv)
+ }
+ }.resume()
+ } else {
+ let path: String = Preferences[.gifFilePath]
+ guard !path.isEmpty,
+ let data = try? Data(contentsOf: URL(fileURLWithPath: path)),
+ let img = NSImage(data: data) else { return }
+ piv.image = img
+ piv.animates = true
+ applyScaling(mode, to: piv)
+ }
+ }
+
+ private func applyScaling(_ mode: Int, to iv: NSImageView) {
+ switch mode {
+ case 1:
+ iv.imageScaling = .scaleAxesIndependently
+ iv.layer?.contentsGravity = .resizeAspectFill
+ case 2:
+ iv.imageScaling = .scaleAxesIndependently
+ iv.layer?.contentsGravity = .resize
+ default:
+ iv.imageScaling = .scaleProportionallyUpOrDown
+ iv.layer?.contentsGravity = .resizeAspect
+ }
+ }
+
+ // MARK: - UI Factory
+
+ private func makeCheckbox(_ title: String, tag: Int, on: Bool) -> NSButton {
+ let btn = NSButton(checkboxWithTitle: title, target: self, action: #selector(checkboxChanged(_:)))
+ btn.tag = tag
+ btn.state = on ? .on : .off
+ return btn
+ }
+
+ private func sectionLabel(_ text: String) -> NSTextField {
+ let lbl = NSTextField(labelWithString: text.uppercased())
+ lbl.font = NSFont.systemFont(ofSize: 10, weight: .semibold)
+ lbl.textColor = NSColor.secondaryLabelColor
+ return lbl
+ }
+
+ private func makeRow(_ label: String, _ control: NSView) -> NSStackView {
+ let lbl = NSTextField(labelWithString: label)
+ lbl.font = NSFont.systemFont(ofSize: 12, weight: .semibold)
+ lbl.setContentHuggingPriority(.defaultHigh, for: .horizontal)
+ let row = NSStackView(views: [lbl, control])
+ row.orientation = .horizontal
+ row.alignment = .firstBaseline
+ row.spacing = 6
+ return row
+ }
+
+ private func makeSeparator() -> NSBox {
+ let sep = NSBox()
+ sep.boxType = .separator
+ return sep
}
}
diff --git a/Status/Sources/Preferences/StatusWidgetPreferencePane/en.lproj/StatusWidgetPreferencePane.strings b/Status/Sources/Preferences/StatusWidgetPreferencePane/en.lproj/StatusWidgetPreferencePane.strings
deleted file mode 100644
index 718b221..0000000
--- a/Status/Sources/Preferences/StatusWidgetPreferencePane/en.lproj/StatusWidgetPreferencePane.strings
+++ /dev/null
@@ -1,24 +0,0 @@
-
-/* Class = "NSTextFieldCell"; title = "EE dd MMM HH:mm"; ObjectID = "6HU-p8-WWc"; */
-"6HU-p8-WWc.title" = "EE dd MMM HH:mm";
-
-/* Class = "NSTextFieldCell"; title = "Time Format:"; ObjectID = "6tU-S4-ovU"; */
-"6tU-S4-ovU.title" = "Time Format:";
-
-/* Class = "NSButtonCell"; title = "Show Power item"; ObjectID = "OGc-Cn-VS5"; */
-"OGc-Cn-VS5.title" = "Show Power item";
-
-/* Class = "NSButton"; ibShadowedToolTip = "Time Format Help"; ObjectID = "QDl-u9-yz3"; */
-"QDl-u9-yz3.ibShadowedToolTip" = "Time Format Help";
-
-/* Class = "NSButtonCell"; title = "Show Battery Icon"; ObjectID = "S9t-Wy-9IV"; */
-"S9t-Wy-9IV.title" = "Show Battery Icon";
-
-/* Class = "NSButtonCell"; title = "Show Date item"; ObjectID = "Zze-FZ-vdx"; */
-"Zze-FZ-vdx.title" = "Show Date item";
-
-/* Class = "NSButtonCell"; title = "Show Wifi item"; ObjectID = "c9g-yA-umL"; */
-"c9g-yA-umL.title" = "Show Wifi item";
-
-/* Class = "NSButtonCell"; title = "Show Battery Percentage"; ObjectID = "zlO-nx-U7P"; */
-"zlO-nx-U7P.title" = "Show Battery Percentage";
diff --git a/Status/Sources/Preferences/StatusWidgetPreferencePane/it.lproj/StatusWidgetPreferencePane.strings b/Status/Sources/Preferences/StatusWidgetPreferencePane/it.lproj/StatusWidgetPreferencePane.strings
deleted file mode 100644
index 1744f36..0000000
--- a/Status/Sources/Preferences/StatusWidgetPreferencePane/it.lproj/StatusWidgetPreferencePane.strings
+++ /dev/null
@@ -1,24 +0,0 @@
-
-/* Class = "NSTextFieldCell"; title = "EE dd MMM HH:mm"; ObjectID = "6HU-p8-WWc"; */
-"6HU-p8-WWc.title" = "EE dd MMM HH:mm";
-
-/* Class = "NSTextFieldCell"; title = "Time Format:"; ObjectID = "6tU-S4-ovU"; */
-"6tU-S4-ovU.title" = "Time Format:";
-
-/* Class = "NSButtonCell"; title = "Show Power item"; ObjectID = "OGc-Cn-VS5"; */
-"OGc-Cn-VS5.title" = "Mostra informazioni sulla batteria";
-
-/* Class = "NSButton"; ibShadowedToolTip = "Time Format Help"; ObjectID = "QDl-u9-yz3"; */
-"QDl-u9-yz3.ibShadowedToolTip" = "Time Format Help";
-
-/* Class = "NSButtonCell"; title = "Show Battery Icon"; ObjectID = "S9t-Wy-9IV"; */
-"S9t-Wy-9IV.title" = "Mostra icona";
-
-/* Class = "NSButtonCell"; title = "Show Date item"; ObjectID = "Zze-FZ-vdx"; */
-"Zze-FZ-vdx.title" = "Mostra la data e l'ora attuale";
-
-/* Class = "NSButtonCell"; title = "Show Wifi item"; ObjectID = "c9g-yA-umL"; */
-"c9g-yA-umL.title" = "Mostra informaziioni sulla connettività Wi-Fi";
-
-/* Class = "NSButtonCell"; title = "Show Battery Percentage"; ObjectID = "zlO-nx-U7P"; */
-"zlO-nx-U7P.title" = "Mostra percentuale di carica";
diff --git a/Status/Sources/Preferences/StatusWidgetPreferencePane/tr.lproj/StatusWidgetPreferencePane.strings b/Status/Sources/Preferences/StatusWidgetPreferencePane/tr.lproj/StatusWidgetPreferencePane.strings
deleted file mode 100644
index 929d812..0000000
--- a/Status/Sources/Preferences/StatusWidgetPreferencePane/tr.lproj/StatusWidgetPreferencePane.strings
+++ /dev/null
@@ -1,24 +0,0 @@
-
-/* Class = "NSTextFieldCell"; title = "EE dd MMM HH:mm"; ObjectID = "6HU-p8-WWc"; */
-"6HU-p8-WWc.title" = "EE dd MMM HH:mm";
-
-/* Class = "NSTextFieldCell"; title = "Time Format:"; ObjectID = "6tU-S4-ovU"; */
-"6tU-S4-ovU.title" = "Time Format:";
-
-/* Class = "NSButtonCell"; title = "Show Power item"; ObjectID = "OGc-Cn-VS5"; */
-"OGc-Cn-VS5.title" = "Güç Ögelerini Göster";
-
-/* Class = "NSButton"; ibShadowedToolTip = "Time Format Help"; ObjectID = "QDl-u9-yz3"; */
-"QDl-u9-yz3.ibShadowedToolTip" = "Time Format Help";
-
-/* Class = "NSButtonCell"; title = "Show Battery Icon"; ObjectID = "S9t-Wy-9IV"; */
-"S9t-Wy-9IV.title" = "Batarya İkonunu Göster";
-
-/* Class = "NSButtonCell"; title = "Show Date item"; ObjectID = "Zze-FZ-vdx"; */
-"Zze-FZ-vdx.title" = "Tarihi Göster";
-
-/* Class = "NSButtonCell"; title = "Show Wifi item"; ObjectID = "c9g-yA-umL"; */
-"c9g-yA-umL.title" = "Wi-Fi'ı Göster";
-
-/* Class = "NSButtonCell"; title = "Show Battery Percentage"; ObjectID = "zlO-nx-U7P"; */
-"zlO-nx-U7P.title" = "Batarya Yüzdesini Göster";
diff --git a/Status/Sources/Preferences/StatusWidgetPreferencePane/zh-Hans.lproj/StatusWidgetPreferencePane.strings b/Status/Sources/Preferences/StatusWidgetPreferencePane/zh-Hans.lproj/StatusWidgetPreferencePane.strings
deleted file mode 100644
index 2ccd3e4..0000000
--- a/Status/Sources/Preferences/StatusWidgetPreferencePane/zh-Hans.lproj/StatusWidgetPreferencePane.strings
+++ /dev/null
@@ -1,24 +0,0 @@
-
-/* Class = "NSTextFieldCell"; title = "EE dd MMM HH:mm"; ObjectID = "6HU-p8-WWc"; */
-"6HU-p8-WWc.title" = "EE dd MMM HH:mm";
-
-/* Class = "NSTextFieldCell"; title = "Time Format:"; ObjectID = "6tU-S4-ovU"; */
-"6tU-S4-ovU.title" = "时间格式:";
-
-/* Class = "NSButtonCell"; title = "Show Power item"; ObjectID = "OGc-Cn-VS5"; */
-"OGc-Cn-VS5.title" = "显示电池";
-
-/* Class = "NSButton"; ibShadowedToolTip = "Time Format Help"; ObjectID = "QDl-u9-yz3"; */
-"QDl-u9-yz3.ibShadowedToolTip" = "时间格式帮助";
-
-/* Class = "NSButtonCell"; title = "Show Battery Icon"; ObjectID = "S9t-Wy-9IV"; */
-"S9t-Wy-9IV.title" = "显示电池图标";
-
-/* Class = "NSButtonCell"; title = "Show Date item"; ObjectID = "Zze-FZ-vdx"; */
-"Zze-FZ-vdx.title" = "显示时间";
-
-/* Class = "NSButtonCell"; title = "Show Wifi item"; ObjectID = "c9g-yA-umL"; */
-"c9g-yA-umL.title" = "显示Wifi";
-
-/* Class = "NSButtonCell"; title = "Show Battery Percentage"; ObjectID = "zlO-nx-U7P"; */
-"zlO-nx-U7P.title" = "显示电池百分比";
diff --git a/Status/Sources/Preferences/StatusWidgetPreferencePane/zh-Hant.lproj/StatusWidgetPreferencePane.strings b/Status/Sources/Preferences/StatusWidgetPreferencePane/zh-Hant.lproj/StatusWidgetPreferencePane.strings
deleted file mode 100644
index f2d632d..0000000
--- a/Status/Sources/Preferences/StatusWidgetPreferencePane/zh-Hant.lproj/StatusWidgetPreferencePane.strings
+++ /dev/null
@@ -1,24 +0,0 @@
-
-/* Class = "NSTextFieldCell"; title = "EE dd MMM HH:mm"; ObjectID = "6HU-p8-WWc"; */
-"6HU-p8-WWc.title" = "EE dd MMM HH:mm";
-
-/* Class = "NSTextFieldCell"; title = "Time Format:"; ObjectID = "6tU-S4-ovU"; */
-"6tU-S4-ovU.title" = "時間格式:";
-
-/* Class = "NSButtonCell"; title = "Show Power item"; ObjectID = "OGc-Cn-VS5"; */
-"OGc-Cn-VS5.title" = "顯示電量";
-
-/* Class = "NSButton"; ibShadowedToolTip = "Time Format Help"; ObjectID = "QDl-u9-yz3"; */
-"QDl-u9-yz3.ibShadowedToolTip" = "時間格式幫助";
-
-/* Class = "NSButtonCell"; title = "Show Battery Icon"; ObjectID = "S9t-Wy-9IV"; */
-"S9t-Wy-9IV.title" = "顯示電池圖示";
-
-/* Class = "NSButtonCell"; title = "Show Date item"; ObjectID = "Zze-FZ-vdx"; */
-"Zze-FZ-vdx.title" = "顯示時間";
-
-/* Class = "NSButtonCell"; title = "Show Wifi item"; ObjectID = "c9g-yA-umL"; */
-"c9g-yA-umL.title" = "顯示 Wi-Fi";
-
-/* Class = "NSButtonCell"; title = "Show Battery Percentage"; ObjectID = "zlO-nx-U7P"; */
-"zlO-nx-U7P.title" = "顯示電池百分比";
diff --git a/Status/Sources/StatusItem.swift b/Status/Sources/StatusItem.swift
old mode 100644
new mode 100755
index b373d09..967ad28
--- a/Status/Sources/StatusItem.swift
+++ b/Status/Sources/StatusItem.swift
@@ -7,6 +7,7 @@
//
import Foundation
+import AppKit
import PockKit
class StatusItemView: PKView {
diff --git a/Status/Sources/StatusWidget.swift b/Status/Sources/StatusWidget.swift
index c605d27..908ce07 100755
--- a/Status/Sources/StatusWidget.swift
+++ b/Status/Sources/StatusWidget.swift
@@ -12,110 +12,106 @@ import PockKit
import TinyConstraints
extension NSImage {
- /// Returns an NSImage snapshot of the passed view in 2x resolution.
- convenience init?(frame: NSRect, view: NSView) {
- guard let bitmapRep = view.bitmapImageRepForCachingDisplay(in: frame) else {
- return nil
- }
- self.init()
- view.cacheDisplay(in: frame, to: bitmapRep)
- addRepresentation(bitmapRep)
- bitmapRep.size = frame.size
- }
+ /// Returns an NSImage snapshot of the passed view.
+ convenience init?(frame: NSRect, view: NSView) {
+ guard let bitmapRep = view.bitmapImageRepForCachingDisplay(in: frame) else { return nil }
+ self.init()
+ view.cacheDisplay(in: frame, to: bitmapRep)
+ addRepresentation(bitmapRep)
+ bitmapRep.size = frame.size
+ }
}
-class StatusWidget: PKWidget {
-
- static var identifier: String = "StatusWidget"
- var customizationLabel: String = "Status"
- var view: NSView!
-
- private var stackView: NSStackView {
- return view as! NSStackView
- }
- private var loadedItems: [StatusItem] = []
-
- var imageForCustomization: NSImage {
- let stackView = NSStackView(frame: .zero)
- stackView.orientation = .horizontal
- stackView.alignment = .centerY
- stackView.distribution = .fill
- stackView.spacing = 8
- if Preferences[.shouldShowLangItem] {
- stackView.addArrangedSubview(SLangItem().view)
- }
- if Preferences[.shouldShowWifiItem] {
- stackView.addArrangedSubview(SWifiItem().view)
- }
- if Preferences[.shouldShowPowerItem] {
- stackView.addArrangedSubview(SPowerItem().view)
- }
- if Preferences[.shouldShowDateItem] {
- stackView.addArrangedSubview(SClockItem().view)
- }
- return NSImage(frame: NSRect(origin: .zero, size: stackView.fittingSize), view: stackView) ?? NSImage()
- }
-
- func prepareForCustomization() {
- clearItems()
- }
-
+class StatusV2Widget: PKWidget {
+
+ static var identifier: String = "EvyatarEshkar.StatusV2Widget"
+ var customizationLabel: String = "Status V2"
+ var view: NSView!
+
+ private var stackView: NSStackView { return view as! NSStackView }
+ private var loadedItems: [StatusItem] = []
+
+ // MARK: - PKWidget
+
+ var imageForCustomization: NSImage {
+ let sv = NSStackView(frame: .zero)
+ sv.orientation = .horizontal
+ sv.alignment = .centerY
+ sv.distribution = .fill
+ sv.spacing = 8
+ if Preferences[.shouldShowLangItem] { sv.addArrangedSubview(SLangItem().view) }
+ if Preferences[.shouldShowWifiItem] { sv.addArrangedSubview(SWifiItem().view) }
+ if Preferences[.shouldShowPowerItem] { sv.addArrangedSubview(SPowerItem().view) }
+ if Preferences[.shouldShowDateItem] { sv.addArrangedSubview(SClockItem().view) }
+ return NSImage(frame: NSRect(origin: .zero, size: sv.fittingSize), view: sv) ?? NSImage()
+ }
+
+ func prepareForCustomization() {
+ clearItems()
+ }
+
required init() {
- view = NSStackView(frame: .zero)
- stackView.orientation = .horizontal
- stackView.alignment = .centerY
- stackView.distribution = .fill
- stackView.spacing = 8
+ view = NSStackView(frame: .zero)
+ stackView.orientation = .horizontal
+ stackView.alignment = .centerY
+ stackView.distribution = .fill
+ stackView.spacing = 8
}
-
+
deinit {
- clearItems()
+ clearItems()
}
-
+
func viewDidAppear() {
- loadStatusElements()
- NotificationCenter.default.addObserver(self, selector: #selector(loadStatusElements), name: .shouldReloadStatusWidget, object: nil)
+ loadStatusElements()
+ NotificationCenter.default.addObserver(
+ self, selector: #selector(loadStatusElements),
+ name: .shouldReloadStatusWidget, object: nil
+ )
}
-
+
func viewWillDisappear() {
clearItems()
- NotificationCenter.default.removeObserver(self)
+ NotificationCenter.default.removeObserver(self)
+ }
+
+ // MARK: - Loading
+
+ private func clearItems() {
+ for v in stackView.arrangedSubviews {
+ stackView.removeArrangedSubview(v)
+ v.removeFromSuperview()
+ }
+ loadedItems.forEach { $0.didUnload() }
+ loadedItems.removeAll()
}
-
- private func clearItems() {
- for view in stackView.arrangedSubviews {
- stackView.removeArrangedSubview(view)
- view.removeFromSuperview()
- }
- for item in loadedItems {
- item.didUnload()
- }
- loadedItems.removeAll()
- }
-
+
@objc private func loadStatusElements() {
- clearItems()
- if Preferences[.shouldShowLangItem] {
- let item = SLangItem()
- loadedItems.append(item)
- stackView.addArrangedSubview(item.view)
- }
- if Preferences[.shouldShowWifiItem] {
- let item = SWifiItem()
- loadedItems.append(item)
- stackView.addArrangedSubview(item.view)
- }
- if Preferences[.shouldShowPowerItem] {
- let item = SPowerItem()
- loadedItems.append(item)
- stackView.addArrangedSubview(item.view)
- }
- if Preferences[.shouldShowDateItem] {
- let item = SClockItem()
- loadedItems.append(item)
- stackView.addArrangedSubview(item.view)
- }
- stackView.height(30)
+ clearItems()
+
+ if Preferences[.shouldShowLangItem] {
+ let item = SLangItem(); loadedItems.append(item)
+ stackView.addArrangedSubview(item.view)
+ }
+ if Preferences[.shouldShowWifiItem] {
+ let item = SWifiItem(); loadedItems.append(item)
+ stackView.addArrangedSubview(item.view)
+ }
+ if Preferences[.shouldShowPowerItem] {
+ let item = SPowerItem(); loadedItems.append(item)
+ stackView.addArrangedSubview(item.view)
+ }
+ if Preferences[.shouldShowDateItem] {
+ let item = SClockItem(); loadedItems.append(item)
+ stackView.addArrangedSubview(item.view)
+ }
+
+ // GIF — rightmost item
+ if Preferences[.shouldShowGif] {
+ let item = SGifItem(); loadedItems.append(item)
+ stackView.addArrangedSubview(item.view)
+ }
+
+ stackView.height(30)
}
-
}
diff --git a/images/Settings.png b/images/Settings.png
new file mode 100644
index 0000000..7677502
Binary files /dev/null and b/images/Settings.png differ
diff --git a/images/TouchBar-Preview-PockStatusV2Widget.gif b/images/TouchBar-Preview-PockStatusV2Widget.gif
new file mode 100644
index 0000000..636e7fa
Binary files /dev/null and b/images/TouchBar-Preview-PockStatusV2Widget.gif differ