Skip to content

Commit

Permalink
New menu bar view
Browse files Browse the repository at this point in the history
  • Loading branch information
zqqf16 committed Feb 3, 2023
1 parent 0041d6b commit 9b9826d
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 34 deletions.
16 changes: 16 additions & 0 deletions Monster.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
F4DFF5E028C991E800246E1F /* GenericConfigHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4DFF5DF28C991E800246E1F /* GenericConfigHelper.swift */; };
F4E11D0228C4DF6200649826 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E11D0128C4DF6200649826 /* Settings.swift */; };
F4EA69F8298CBC1400FA2E96 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EA69F7298CBC1400FA2E96 /* SettingsView.swift */; };
F4EA6A01298CE40200FA2E96 /* MenuBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EA6A00298CE40200FA2E96 /* MenuBar.swift */; };
F4EA6A03298CFB5400FA2E96 /* NSApplication+Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EA6A02298CFB5400FA2E96 /* NSApplication+Window.swift */; };
F4EE4CF628D1941600A0003E /* ShareFolderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EE4CF528D1941600A0003E /* ShareFolderView.swift */; };
F4EE4CF828D1950100A0003E /* VMShareDirectory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4EE4CF728D1950100A0003E /* VMShareDirectory.swift */; };
F4F2AF8F28C38DD500C3A5A3 /* VMBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F2AF8E28C38DD500C3A5A3 /* VMBundle.swift */; };
Expand Down Expand Up @@ -99,6 +101,8 @@
F4DFF5DF28C991E800246E1F /* GenericConfigHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericConfigHelper.swift; sourceTree = "<group>"; };
F4E11D0128C4DF6200649826 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
F4EA69F7298CBC1400FA2E96 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
F4EA6A00298CE40200FA2E96 /* MenuBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBar.swift; sourceTree = "<group>"; };
F4EA6A02298CFB5400FA2E96 /* NSApplication+Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication+Window.swift"; sourceTree = "<group>"; };
F4EE4CF528D1941600A0003E /* ShareFolderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareFolderView.swift; sourceTree = "<group>"; };
F4EE4CF728D1950100A0003E /* VMShareDirectory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMShareDirectory.swift; sourceTree = "<group>"; };
F4F2AF8E28C38DD500C3A5A3 /* VMBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMBundle.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -168,6 +172,7 @@
F4D5984028A9EDD100F9958B /* View */ = {
isa = PBXGroup;
children = (
F4EA69FD298CE3D100FA2E96 /* MenuBarExtra */,
F4EA69F6298CBC0400FA2E96 /* Settings */,
F435FFA428C20759006D5671 /* ConfigItems */,
F4FFF28728A38360003B75F9 /* ContentView.swift */,
Expand Down Expand Up @@ -214,6 +219,7 @@
F435FF9E28C1DC90006D5671 /* Measurement.swift */,
F435FFA228C1F909006D5671 /* Unit.swift */,
F4F577AF28CB6F0A0040E394 /* NSImage.swift */,
F4EA6A02298CFB5400FA2E96 /* NSApplication+Window.swift */,
);
path = Utils;
sourceTree = "<group>";
Expand All @@ -226,6 +232,14 @@
path = Settings;
sourceTree = "<group>";
};
F4EA69FD298CE3D100FA2E96 /* MenuBarExtra */ = {
isa = PBXGroup;
children = (
F4EA6A00298CE40200FA2E96 /* MenuBar.swift */,
);
path = MenuBarExtra;
sourceTree = "<group>";
};
F4FFF27928A38360003B75F9 = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -445,11 +459,13 @@
F4EE4CF828D1950100A0003E /* VMShareDirectory.swift in Sources */,
F4376BD228C0A33400E7B65A /* EntranceItem.swift in Sources */,
F4D6880F28C59FFE003544A6 /* VMConfigHelper.swift in Sources */,
F4EA6A01298CE40200FA2E96 /* MenuBar.swift in Sources */,
F4F577AA28CAE7D00040E394 /* VirtualMachine.swift in Sources */,
F435FF9B28C1ABEA006D5671 /* OperatingSystem.swift in Sources */,
F4D6881128C62F06003544A6 /* FileButton.swift in Sources */,
F4BE60A628A67CBC007340AD /* VirtualMachineView.swift in Sources */,
F4FFF2B228A39B4F003B75F9 /* VMConfig.swift in Sources */,
F4EA6A03298CFB5400FA2E96 /* NSApplication+Window.swift in Sources */,
F4FFF28628A38360003B75F9 /* MonsterApp.swift in Sources */,
F4D90FC628AE282200E11588 /* VMInstance.swift in Sources */,
F4FFF2B828A3F3C6003B75F9 /* Sidebar.swift in Sources */,
Expand Down
13 changes: 13 additions & 0 deletions Monster/Model/ConfigUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,17 @@ extension VirtualMachine.State {
case .stopped, .stopping: return .systemGray
}
}
var name: String {
switch self {
case .error: return "Error"
case .stopped: return "Stopped"
case .running: return "Running"
case .paused: return "Paused"
case .starting: return "Starting"
case .pausing: return "Pausing"
case .resuming: return "Resuming"
case .stopping: return "Stopping"
case .installing: return "Installing"
}
}
}
28 changes: 7 additions & 21 deletions Monster/MonsterApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// disable tabbing
NSWindow.allowsAutomaticWindowTabbing = false

createStatusItem()

let preview = ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"]
if preview != "1" {
// Do not active app during xcode previewing
Expand All @@ -51,25 +49,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NSApp.setDockIconHidden(!AppSettings.standard.showDockIcon)
})
}

private func createStatusItem() {
// waiting for MenuBarExtra ...
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
let statusButton = statusItem?.button
statusButton?.image = NSImage(systemSymbolName: "m.square.fill", accessibilityDescription: nil)
statusButton?.image = NSImage(named: "StatusBarIcon")
statusButton?.action = #selector(AppDelegate.showWindow)
}

@objc func showWindow() {
debugPrint("show window")
if !NSApp.windows.hasSwiftUIWindow {
debugPrint("Create a new window")
NSWorkspace.shared.open(URL(string: "monster://main")!)
}

NSApp.activate(ignoringOtherApps: true)
}
}

@main
Expand Down Expand Up @@ -111,6 +90,13 @@ struct MonsterApp: App {
Settings {
SettingsView()
}

// Menu bar item
MenuBarExtra("Monster", image: "StatusBarIcon") {
MenuBar()
.environmentObject(store)
}
.menuBarExtraStyle(.window)
}

private func open(url: URL) {
Expand Down
24 changes: 24 additions & 0 deletions Monster/Utils/NSApplication+Window.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// NSApplication+Window.swift
// Monster
//
// Created by zqqf16 on 2023/2/3.
// Copyright © 2023 zqqf16. All rights reserved.
//

import Cocoa

extension NSApplication {
func closeMenuBarExtraWindow() {
windows.forEach { window in
if window.isExcludedFromWindowsMenu {
window.close()
}
}
}

func showMainWindow() {
self.closeMenuBarExtraWindow()
NSWorkspace.shared.open(URL(string: "monster://main")!)
}
}
4 changes: 4 additions & 0 deletions Monster/Utils/NSWindow+SwiftUI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ extension NSWindow {
var isSwiftUIWindow: Bool {
return String(describing: type(of: self)) == "AppKitWindow"
}

var isMenuBarExtraWindow: Bool {
return String(describing: type(of: self)).contains("MenuBarExtraWindow")
}
}

extension Array where Element == NSWindow {
Expand Down
11 changes: 7 additions & 4 deletions Monster/View/ConfigItems/SnapshotView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@ struct SnapshotView: View {
.resizable()
.aspectRatio(contentMode: .fit)
} else {
Rectangle()
.fill(.secondary.opacity(0.1))
ZStack(alignment: .center) {
Rectangle()
.fill(.secondary.opacity(0.1))
Image(vm.config.icon)
}
}
}
.cornerRadius(8)

buttons
.offset(y: -4)
}
.cornerRadius(8)
.aspectRatio(16 / 9, contentMode: .fit)
.frame(height: 120)
}
Expand All @@ -50,7 +53,6 @@ struct SnapshotView: View {
previewButton
}
.padding(EdgeInsets(top: 4, leading: 12, bottom: 4, trailing: 12))
// .background(.gray.opacity(0.5))
.cornerRadius(4)
}

Expand Down Expand Up @@ -89,6 +91,7 @@ struct SnapshotView: View {
private var previewButton: some View {
createButton("display") {
openWindow(value: vm.id)
NSApp.activate(ignoringOtherApps: true)
}
}
}
Expand Down
144 changes: 144 additions & 0 deletions Monster/View/MenuBarExtra/MenuBar.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//
// MenuBar.swift
// Monster
//
// Created by zqqf16 on 2023/2/3.
// Copyright © 2023 zqqf16. All rights reserved.
//

import SwiftUI

struct ConfigInfoView: View {
@ObservedObject var vm: VirtualMachine

var body: some View {
HStack(alignment: .lastTextBaseline, spacing: 2) {
Circle()
.fill(Color(nsColor: vm.state.color))
.frame(width: 8)

Spacer(minLength: 6)

Text("\(vm.config.diskSize.gb)")
.font(.system(.body, design: .monospaced))
Text("G DISK")
.foregroundColor(.primary.opacity(0.7))
.font(.subheadline)

Spacer(minLength: 4)

Text("\(vm.config.memorySize.mb)")
.font(.system(.body, design: .monospaced))
Text("M MEM")
.foregroundColor(.primary.opacity(0.7))
.font(.subheadline)

Spacer(minLength: 4)

Text("\(vm.config.cpuCount.count)")
.font(.system(.body, design: .monospaced))
Text("CPU")
.foregroundColor(.primary.opacity(0.7))
.font(.subheadline)
}
}
}

struct MenuItem: View {
@ObservedObject var vm: VirtualMachine
@Environment(\.openWindow) private var openWindow

@State var isHovering = false

var body: some View {
GroupBox {
HStack(spacing: 8) {
iconView
.frame(width: 48, height: 48)
descriptionView
.frame(maxWidth: .infinity, alignment: .leading)
previewButton
.frame(width: 36)
}
}
.onHover { onHover in
withAnimation {
isHovering = onHover
}
}
.scaleEffect(isHovering ? 1.02 : 1.0)
}

@ViewBuilder
private var iconView: some View {
ZStack(alignment: .center) {
if let snapshot = vm.snapshot {
Image(nsImage: snapshot)
.resizable()
.aspectRatio(contentMode: .fit)
} else {
Image(vm.config.icon)
.resizable()
.aspectRatio(contentMode: .fit)
}
}.cornerRadius(4)
}

@ViewBuilder
private var descriptionView: some View {
VStack(alignment: .leading, spacing: 2) {
Text(vm.name)
.font(.title2)
ConfigInfoView(vm: vm)
}
}

@ViewBuilder
private var previewButton: some View {
Button {
NSApp.closeMenuBarExtraWindow()
openWindow(value: vm.id)
NSApp.activate(ignoringOtherApps: true)
} label: {
Image(systemName: "display")
}
.buttonStyle(.plain)
}
}

struct MenuBar: View {
@EnvironmentObject private var store: Store

var body: some View {
VStack {
ForEach(store.vms) { vm in
MenuItem(vm: vm)
.onTapGesture {
store.selectedVM = vm
showMainWindow()
}
.frame(maxWidth: .infinity)
}
Divider()
Button {
showMainWindow()
} label: {
Text("Show window")
}
.buttonStyle(.borderless)
}
.fixedSize(horizontal: true, vertical: false)
.padding(12)
}

func showMainWindow() {
NSApp.closeMenuBarExtraWindow()
NSApp.showMainWindow()
}
}

struct MenuBar_Previews: PreviewProvider {
static var previews: some View {
MenuBar()
}
}
6 changes: 3 additions & 3 deletions Monster/View/SetupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct SetupView: View {
@EnvironmentObject private var store: Store
@Environment(\.dismiss) private var dismiss

@State var linuxDictribution: OperatingSystem?
@State var linuxDistribution: OperatingSystem?

@State var error: Error? = nil

Expand Down Expand Up @@ -64,10 +64,10 @@ struct SetupView: View {
withAnimation {
switch value {
case .macOS:
linuxDictribution = config.os
linuxDistribution = config.os
os.wrappedValue = .macOS
case .linux:
os.wrappedValue = linuxDictribution ?? .linux
os.wrappedValue = linuxDistribution ?? .linux
case .import:
openPanel()
case .none:
Expand Down
7 changes: 1 addition & 6 deletions Monster/View/VirtualMachineView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@ struct VirtualMachineView: View {
.toolbar {
toolbar
}
.onAppear {
if vm.state == .stopped {
execute(vm.run)
}
}
.onReceive(vm.$state, perform: { state in
withAnimation {
showBanner = state != .running
Expand Down Expand Up @@ -143,7 +138,7 @@ struct VirtualMachineView: View {
if let error = error as? Failure {
self.currentError = error
} else {
self.currentError = Failure("Unknow error", reason: error)
self.currentError = Failure("Unknown error", reason: error)
}
withAnimation {
self.showBanner = true
Expand Down

0 comments on commit 9b9826d

Please sign in to comment.