86 lines
2.7 KiB
Swift
86 lines
2.7 KiB
Swift
import AppKit
|
|
import SwiftUI
|
|
|
|
final class FloatingWidgetWindow: NSPanel {
|
|
init() {
|
|
super.init(
|
|
contentRect: NSRect(x: 0, y: 0, width: 140, height: 70),
|
|
styleMask: [.borderless, .nonactivatingPanel],
|
|
backing: .buffered,
|
|
defer: false
|
|
)
|
|
|
|
isFloatingPanel = true
|
|
level = .floating
|
|
collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
|
isOpaque = false
|
|
backgroundColor = .clear
|
|
hasShadow = true
|
|
isMovableByWindowBackground = true
|
|
hidesOnDeactivate = false
|
|
animationBehavior = .none
|
|
|
|
if let screen = NSScreen.main {
|
|
let screenFrame = screen.visibleFrame
|
|
setFrameOrigin(NSPoint(x: screenFrame.maxX - 100, y: screenFrame.minY + 200))
|
|
}
|
|
|
|
contentView = NSHostingView(rootView: FloatingWidgetView())
|
|
}
|
|
|
|
override var canBecomeKey: Bool { false }
|
|
override var canBecomeMain: Bool { false }
|
|
}
|
|
|
|
struct FloatingWidgetView: View {
|
|
@State private var bugStore = BugStore.shared
|
|
|
|
private var priorityCounts: [(BugPriority, Int)] {
|
|
let active = bugStore.activeBugs
|
|
return BugPriority.allCases
|
|
.filter { $0 != .unknown }
|
|
.map { priority in
|
|
(priority, active.filter { $0.priority == priority }.count)
|
|
}
|
|
.filter { $0.1 > 0 }
|
|
}
|
|
|
|
var body: some View {
|
|
Button {
|
|
AppDelegate.shared?.togglePopover()
|
|
} label: {
|
|
VStack(spacing: 4) {
|
|
HStack(spacing: 4) {
|
|
Image(systemName: "ant.fill")
|
|
.font(.caption)
|
|
Text("\(bugStore.activeBugs.count)")
|
|
.font(.title3)
|
|
.fontWeight(.bold)
|
|
}
|
|
if !priorityCounts.isEmpty {
|
|
HStack(spacing: 6) {
|
|
ForEach(priorityCounts, id: \.0) { priority, count in
|
|
HStack(spacing: 2) {
|
|
Circle()
|
|
.fill(priority.color)
|
|
.frame(width: 6, height: 6)
|
|
Text("\(count)")
|
|
.font(.caption2)
|
|
.fontWeight(.medium)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 14)
|
|
.padding(.vertical, 10)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 14)
|
|
.fill(.ultraThinMaterial)
|
|
.shadow(color: .black.opacity(0.15), radius: 8, x: 0, y: 4)
|
|
)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|