feat(UI): 改进菜单界面布局和样式

重构菜单界面,添加应用标题和版本信息,优化按钮和状态显示样式
使用卡片式布局和阴影效果提升整体视觉体验
简化状态显示逻辑,增加更直观的状态指示图标
This commit is contained in:
2025-12-27 18:25:29 +08:00
parent 8eb0086779
commit a928c050b9
3 changed files with 99 additions and 55 deletions

View File

@@ -14,7 +14,7 @@ class AppState: ObservableObject {
// //
private var monitorTimer: Timer? private var monitorTimer: Timer?
private var caffeinateProcess: Process? private var caffeinateProcess: Process? // 使Process
// //
private let idleThreshold: TimeInterval = 300 // 5 private let idleThreshold: TimeInterval = 300 // 5
@@ -82,19 +82,18 @@ class AppState: ObservableObject {
private func getSystemIdleTime() -> TimeInterval { private func getSystemIdleTime() -> TimeInterval {
// //
var onPort: mach_port_t = 0 var onPort: mach_port_t = 0
var prop: Unmanaged<CFTypeRef>?
// IO Master Port // IO Master Port
let kr = IOMainPort(0, &onPort) let kr = IOMainPort(0, &onPort)
guard kr == KERN_SUCCESS else { return 0 } guard kr == KERN_SUCCESS else { return 0 }
// // HID HIDIdleTime
let powerService = IOServiceGetMatchingService(onPort, IOServiceMatching("IOPMPowerSource")) let hidSystem = IOServiceGetMatchingService(onPort, IOServiceMatching("IOHIDSystem"))
guard powerService != 0 else { return 0 } guard hidSystem != 0 else { return 0 }
// //
let result = IORegistryEntryCreateCFProperty(powerService, "HIDIdleTime" as CFString, kCFAllocatorDefault, 0) let result = IORegistryEntryCreateCFProperty(hidSystem, "HIDIdleTime" as CFString, kCFAllocatorDefault, 0)
IOObjectRelease(powerService) IOObjectRelease(hidSystem)
guard let data = result?.takeRetainedValue() as? Data else { return 0 } guard let data = result?.takeRetainedValue() as? Data else { return 0 }
@@ -111,12 +110,21 @@ class AppState: ObservableObject {
guard caffeinateProcess == nil else { return } guard caffeinateProcess == nil else { return }
print("用户活动,启动 caffeinate 阻止睡眠...") print("用户活动,启动 caffeinate 阻止睡眠...")
// caffeinate
caffeinateProcess = Process() caffeinateProcess = Process()
caffeinateProcess?.executableURL = URL(fileURLWithPath: "/usr/bin/caffeinate") caffeinateProcess?.executableURL = URL(fileURLWithPath: "/usr/bin/caffeinate")
caffeinateProcess?.arguments = ["-d"] // -d caffeinateProcess?.arguments = ["-d"] // -d
//
let outputPipe = Pipe()
caffeinateProcess?.standardOutput = outputPipe
caffeinateProcess?.standardError = outputPipe
caffeinateProcess?.standardInput = Pipe()
do { do {
try caffeinateProcess?.run() try caffeinateProcess?.run()
print("成功启动 caffeinate 进程")
} catch { } catch {
print("启动 caffeinate 失败: \(error)") print("启动 caffeinate 失败: \(error)")
caffeinateProcess = nil caffeinateProcess = nil
@@ -124,11 +132,16 @@ class AppState: ObservableObject {
} }
private func stopCaffeinate() { private func stopCaffeinate() {
guard caffeinateProcess != nil else { return } guard let process = caffeinateProcess else { return }
print("用户空闲或功能关闭,停止 caffeinate.") print("用户空闲或功能关闭,停止 caffeinate 进程")
caffeinateProcess?.terminate()
//
process.terminate()
// 退
caffeinateProcess = nil caffeinateProcess = nil
print("已重置 caffeinate 进程引用")
} }
// MARK: - UI State Update // MARK: - UI State Update

View File

@@ -1,24 +0,0 @@
//
// ContentView.swift
// PowerBar
//
// Created by Leo on 2025/12/27.
//
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
}
}
#Preview {
ContentView()
}

View File

@@ -32,42 +32,88 @@ struct MenuContent: View {
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 10) { VStack(alignment: .leading, spacing: 8) {
//
HStack {
Image(systemName: "power")
.foregroundColor(.blue)
VStack(alignment: .leading, spacing: 2) {
Text("PowerBar")
.font(.headline)
.fontWeight(.semibold)
Text("v1.0.0")
.font(.caption2)
.foregroundColor(.secondary)
}
}
.padding(.bottom, 4)
// //
Button(action: { Button(action: {
// //
appState.toggleFunction() appState.toggleFunction()
}) { }) {
HStack { HStack(spacing: 12) {
Image(systemName: appState.isFunctionEnabled ? "stop.circle.fill" : "play.circle.fill") Image(systemName: appState.isFunctionEnabled ? "stop.circle.fill" : "play.circle.fill")
.foregroundColor(appState.isFunctionEnabled ? .red : .green) .foregroundColor(appState.isFunctionEnabled ? .red : .green)
.frame(width: 20) .font(.title2)
Text(appState.isFunctionEnabled ? "关闭常亮" : "开启常亮") VStack(alignment: .leading, spacing: 2) {
Text(appState.isFunctionEnabled ? "关闭常亮" : "开启常亮")
.font(.body)
Text(appState.isFunctionEnabled ? "系统将恢复自动睡眠" : "防止系统进入睡眠状态")
.font(.caption2)
.foregroundColor(.secondary)
}
} }
.frame(maxWidth: .infinity, alignment: .leading)
.padding(8)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(appState.isFunctionEnabled ? Color.red.opacity(0.1) : Color.green.opacity(0.1))
)
} }
.buttonStyle(.plain)
Divider() Divider()
// //
HStack { VStack(alignment: .leading, spacing: 4) {
Text("当前状态:") Text("当前状态")
.font(.caption)
.foregroundColor(.secondary) .foregroundColor(.secondary)
Spacer() HStack {
if appState.isFunctionEnabled { if appState.isFunctionEnabled {
Text(appState.isUserIdle ? "空闲" : "活动中") HStack(spacing: 6) {
.foregroundColor(appState.isUserIdle ? .orange : .green) Circle()
.fill(appState.isUserIdle ? Color.orange : Color.green)
.frame(width: 8, height: 8)
Text(appState.isUserIdle ? "空闲" : "活动中")
.font(.body)
// caffeinate // caffeinate
if !appState.isUserIdle { if !appState.isUserIdle {
Text(" (保护中)") Text("(保护中)")
.foregroundColor(.green) .font(.caption2)
.foregroundColor(.secondary)
}
}
} else {
HStack(spacing: 6) {
Circle()
.fill(Color.gray)
.frame(width: 8, height: 8)
Text("已关闭")
.font(.body)
}
} }
} else { Spacer()
Text("已关闭")
.foregroundColor(.gray)
} }
} }
.font(.caption) .padding(8)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.secondary.opacity(0.05))
)
Divider() Divider()
@@ -75,8 +121,17 @@ struct MenuContent: View {
Button("退出 PowerBar") { Button("退出 PowerBar") {
NSApp.terminate(nil) NSApp.terminate(nil)
} }
.buttonStyle(.plain)
.font(.caption)
.foregroundColor(.secondary)
.padding(4)
} }
.padding(8) .padding(12)
.frame(minWidth: 220) .frame(minWidth: 280)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color.white)
.shadow(radius: 4)
)
} }
} }