Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion Barik/Config/ConfigManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ final class ConfigManager: ObservableObject {
let rootToml = try decoder.decode(RootToml.self, from: content)
DispatchQueue.main.async {
self.config = Config(rootToml: rootToml)

// Notify about config change for widget activation
NotificationCenter.default.post(name: NSNotification.Name("ConfigChanged"), object: nil)
}
} catch {
initError = "Error parsing TOML file: \(error.localizedDescription)"
Expand All @@ -74,6 +77,9 @@ final class ConfigManager: ObservableObject {
"spacer",
"default.network",
"default.battery",
"default.cpuram",
"default.networkactivity",
"default.performance",
"divider",
# { "default.time" = { time-zone = "America/Los_Angeles", format = "E d, hh:mm" } },
"default.time"
Expand All @@ -93,10 +99,25 @@ final class ConfigManager: ObservableObject {
format = "E d, J:mm"
calendar.format = "J:mm"

calendar.show-events = true
calendar.show-events = false
# calendar.allow-list = ["Home", "Personal"] # show only these calendars
# calendar.deny-list = ["Work", "Boss"] # show all calendars except these

[widgets.default.cpuram]
show-icon = false
cpu-warning-level = 70
cpu-critical-level = 90
ram-warning-level = 70
ram-critical-level = 90

[widgets.default.networkactivity]
# No specific configuration options yet

[widgets.default.performance]
# Performance mode widget - replaces volume widget
# Controls energy consumption by adjusting update intervals
# Modes: battery-saver (default), balanced, max-performance

[popup.default.time]
view-variant = "box"

Expand Down
10 changes: 9 additions & 1 deletion Barik/Config/ConfigModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -246,23 +246,31 @@ struct AerospaceConfig: Decodable {
}
}

enum MenuBarPosition: String, Decodable, CaseIterable {
case top = "top"
case bottom = "bottom"
}

struct ExperimentalConfig: Decodable {
let foreground: ForegroundConfig
let background: BackgroundConfig
let position: MenuBarPosition

enum CodingKeys: String, CodingKey {
case foreground, background
case foreground, background, position
}

init() {
self.foreground = ForegroundConfig()
self.background = BackgroundConfig()
self.position = .top
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
foreground = try container.decodeIfPresent(ForegroundConfig.self, forKey: .foreground) ?? ForegroundConfig()
background = try container.decodeIfPresent(BackgroundConfig.self, forKey: .background) ?? BackgroundConfig()
position = try container.decodeIfPresent(MenuBarPosition.self, forKey: .position) ?? .top
}
}

Expand Down
4 changes: 2 additions & 2 deletions Barik/MenuBarPopup/MenuBarPopup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class MenuBarPopup {
MenuBarPopupView {
content()
}
.position(x: rect.midX)
.position(x: rect.midX, y: rect.midY)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.id(UUID())
Expand All @@ -95,7 +95,7 @@ class MenuBarPopup {
MenuBarPopupView {
content()
}
.position(x: rect.midX)
.position(x: rect.midX, y: rect.midY)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
)
Expand Down
18 changes: 14 additions & 4 deletions Barik/MenuBarPopup/MenuBarPopupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ struct MenuBarPopupView<Content: View>: View {
}

var body: some View {
ZStack(alignment: .topTrailing) {
let isBottom = configManager.config.experimental.position == .bottom

ZStack(alignment: isBottom ? .bottomTrailing : .topTrailing) {
content
.background(Color.black)
.cornerRadius(((1.0 - animationValue) * 1) + 40)
.padding(.top, foregroundHeight + 5)
.offset(x: computedOffset, y: computedYOffset)
.padding(isBottom ? .bottom : .top, foregroundHeight + 5)
.offset(x: computedOffset, y: isBottom ? -computedYOffset : computedYOffset)
.shadow(radius: 30)
.blur(radius: (1.0 - (0.1 + 0.9 * animationValue)) * 20)
.scaleEffect(x: 0.2 + 0.8 * animationValue, y: animationValue)
Expand Down Expand Up @@ -151,7 +153,15 @@ struct MenuBarPopupView<Content: View>: View {
}

var computedYOffset: CGFloat {
return viewFrame.height / 2
let isBottom = configManager.config.experimental.position == .bottom

if isBottom {
// For bottom positioning, use the original logic
return viewFrame.height / 2
} else {
// For top positioning, reduce the offset to bring popups closer
return viewFrame.height / 2 - 65 // Bring closer to top menu bar
}
}
}

Expand Down
72 changes: 72 additions & 0 deletions Barik/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
"strings" : {
"?%@?" : {
"shouldTranslate" : false
},
"%@ GB" : {

},
"%lld" : {
"shouldTranslate" : false
},
"%lld%%" : {

},
"ALL_DAY" : {
"extractionState" : "manual",
Expand Down Expand Up @@ -131,6 +137,9 @@
}
}
}
},
"Changes update intervals for widgets to optimize energy consumption" : {

},
"Channel: %@" : {
"localizations" : {
Expand Down Expand Up @@ -255,6 +264,21 @@
}
}
}
},
"CPU" : {

},
"CPU Usage" : {

},
"Current keyboard layout: %@" : {

},
"Current: %@" : {

},
"Download" : {

},
"EMPTY_EVENTS" : {
"extractionState" : "manual",
Expand Down Expand Up @@ -504,6 +528,24 @@
}
}
}
},
"Idle" : {

},
"Keyboard Layout" : {

},
"Memory Usage" : {

},
"Mute" : {

},
"Network Activity" : {

},
"No additional layouts available" : {

},
"Noise: %lld" : {
"localizations" : {
Expand Down Expand Up @@ -628,6 +670,15 @@
}
}
}
},
"Performance Mode" : {

},
"Performance Mode: %@" : {

},
"RAM" : {

},
"RSSI: %lld" : {
"localizations" : {
Expand Down Expand Up @@ -876,6 +927,15 @@
}
}
}
},
"Switch to Next Layout" : {

},
"System" : {

},
"System Monitor" : {

},
"TODAY" : {
"extractionState" : "manual",
Expand Down Expand Up @@ -1126,6 +1186,9 @@
}
}
}
},
"Unmute" : {

},
"Update" : {
"localizations" : {
Expand Down Expand Up @@ -1374,6 +1437,12 @@
}
}
}
},
"Upload" : {

},
"User" : {

},
"v%@ Changelog" : {
"localizations" : {
Expand Down Expand Up @@ -1498,6 +1567,9 @@
}
}
}
},
"Volume" : {

},
"What's new" : {
"localizations" : {
Expand Down
93 changes: 93 additions & 0 deletions Barik/Utils/WidgetActivationManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import Foundation
import SwiftUI

/// Manages which widgets are actively displayed and controls their lifecycle
class WidgetActivationManager: ObservableObject {
static let shared = WidgetActivationManager()

@Published private(set) var activeWidgets: Set<String> = []
private let configManager = ConfigManager.shared

private init() {
// Force immediate update on initialization
updateActiveWidgets()

// Listen for config changes
NotificationCenter.default.addObserver(
self,
selector: #selector(configDidChange),
name: NSNotification.Name("ConfigChanged"),
object: nil
)

// Trigger initial activation after a brief delay to ensure everything is ready
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.updateActiveWidgets()
}
}

@objc private func configDidChange() {
updateActiveWidgets()
}

private func updateActiveWidgets() {
let displayedWidgets = configManager.config.rootToml.widgets.displayed
let newActiveWidgets = Set(displayedWidgets.map { $0.id })

print("WidgetActivationManager: Active widgets updated to: \(newActiveWidgets)")

DispatchQueue.main.async {
self.activeWidgets = newActiveWidgets
}

// Notify about widget activation changes
NotificationCenter.default.post(
name: NSNotification.Name("WidgetActivationChanged"),
object: newActiveWidgets
)
}

/// Checks if a widget with the given ID is currently active (displayed)
func isWidgetActive(_ widgetId: String) -> Bool {
let isActive = activeWidgets.contains(widgetId)
print("WidgetActivationManager: Checking if \(widgetId) is active: \(isActive)")
return isActive
}

/// Get all active widget IDs
func getActiveWidgets() -> Set<String> {
return activeWidgets
}
}

/// A protocol for widgets that can be conditionally activated
protocol ConditionallyActivatableWidget {
func activate()
func deactivate()
var widgetId: String { get }
}

/// Extension to help with conditional widget activation
extension ObservableObject {
func activateIfNeeded(widgetId: String) {
let activationManager = WidgetActivationManager.shared

// Only activate if the widget is being displayed
if activationManager.isWidgetActive(widgetId) {
if let activatable = self as? ConditionallyActivatableWidget {
activatable.activate()
}
}
}

func deactivateIfNeeded(widgetId: String) {
let activationManager = WidgetActivationManager.shared

// Deactivate if the widget is not being displayed
if !activationManager.isWidgetActive(widgetId) {
if let activatable = self as? ConditionallyActivatableWidget {
activatable.deactivate()
}
}
}
}
Loading